Recoil - A state management library for React
Recoil is a state management library built specifically for React applications that introduces an atomic approach to managing both local and global state. Created by Facebook, it addresses performance and architectural limitations found in solutions like Redux and the Context API by breaking state into small, independent units called atoms that components can subscribe to individually.
The library uses two core primitives: atoms (writable units of state) and selectors (pure functions that compute derived state). This design enables fine-grained subscriptions where only components using specific pieces of state re-render when that state changes, avoiding the cascade of re-renders common in traditional global state solutions. Recoil was designed from the ground up to work with React's Concurrent Mode and Suspense, making it a forward-compatible choice for modern React applications.
With over 400,000 weekly downloads, Recoil has gained adoption among teams building complex React applications that need performant state management without excessive boilerplate. It's particularly valuable in applications with dynamic state graphs where state structure can't be known at build time, as atoms can be created at runtime. The library works exclusively with React functional components and hooks, requiring developers to have migrated away from class components.
Recoil provides built-in solutions for async data fetching through async selectors, state persistence, and development tools for debugging. While it's production-ready, developers should note that it's still evolving and occasionally introduces breaking changes between versions, though the team maintains backward compatibility as a priority.
import { atom, selector, useRecoilState, useRecoilValue, RecoilRoot } from 'recoil';
import React from 'react';
// Define an atom for storing todo items
const todoListState = atom({
key: 'todoListState',
default: [],
});
// Define a selector for filtering completed todos
const completedTodosState = selector({
key: 'completedTodosState',
get: ({ get }) => {
const todos = get(todoListState);
return todos.filter(todo => todo.completed);
},
});
// Async selector for fetching user data
const currentUserState = selector({
key: 'currentUserState',
get: async () => {
const response = await fetch('https://api.example.com/user');
return response.json();
},
});
function TodoList() {
const [todos, setTodos] = useRecoilState(todoListState);
const completedTodos = useRecoilValue(completedTodosState);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<div>
<h2>Todos: {todos.length} (Completed: {completedTodos.length})</h2>
<button onClick={() => addTodo('New Task')}>Add Todo</button>
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
function App() {
return (
<RecoilRoot>
<TodoList />
</RecoilRoot>
);
}
export default App;Large-scale React applications with complex state: Apps with many interdependent pieces of state benefit from Recoil's graph-based architecture where selectors automatically update when their dependencies change, avoiding manual subscription management.
Applications requiring optimized re-renders: When you have a large component tree but only want specific components to re-render on state changes, Recoil's atom subscription model ensures surgical updates without context cascading or selector equality checks.
Data fetching and async state management: Building dashboards or data-heavy UIs where components need to fetch and cache data asynchronously becomes simpler with async selectors that integrate with React Suspense, eliminating loading state boilerplate.
Real-time collaborative applications: Apps like document editors or dashboards with live updates can model each piece of editable state as an atom, allowing granular subscriptions and updates without re-rendering unaffected parts of the UI.
Forms with dependent fields: Complex forms where field visibility, validation, or options depend on other field values can use selectors to compute derived state, keeping form logic declarative and centralized rather than scattered across components.
👻 Primitive and flexible state management for React
Simple, scalable state management.
Predictable state container for JavaScript apps
🧙 Valtio makes proxy-state simple for React and Vanilla
🐻 Bear necessities for state management in React
npm install recoilpnpm add recoilbun add recoil