Skip to content

What it is

Recoil is a state management library for React that provides a simple and flexible way to share state across components. It allows for fine-grained state updates, derived state, and asynchronous selectors while integrating seamlessly with React's concurrent features.

Recoil uses atoms to represent shared state and selectors to derive or transform state. Components subscribe to atoms or selectors via hooks, automatically re-rendering when state changes. It supports both synchronous and asynchronous derived state.

Installation

npm install recoil

Getting started

The smallest useful thing you can do with it, and what each part means.

Creating an atom
import { atom } from 'recoil';

export const countState = atom({
  key: 'countState', // unique ID
  default: 0,
});
Defines a Recoil atom representing a piece of state called `countState` with a default value of 0.
Using an atom in a component
import { useRecoilState } from 'recoil';
import { countState } from './store';

function Counter() {
  const [count, setCount] = useRecoilState(countState);
  return (
    <div>
      <button onClick={() => setCount(count - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}
Uses the `useRecoilState` hook to read and update the `countState` atom in a React component.

Advanced usage

Where the library earns its place over a simpler alternative.

Derived state with selectors
import { selector } from 'recoil';
import { countState } from './store';

export const doubleCountState = selector({
  key: 'doubleCountState',
  get: ({ get }) => {
    const count = get(countState);
    return count * 2;
  },
});
Defines a selector that derives a new piece of state (`doubleCountState`) based on the value of `countState`.
Using an async selector
import { selector } from 'recoil';

export const userDataState = selector({
  key: 'userDataState',
  get: async () => {
    const response = await fetch('https://api.example.com/user');
    return response.json();
  },
});
Shows how to define a selector that fetches data asynchronously.
Multiple atoms and selectors
import { atom, selector, useRecoilValue } from 'recoil';
const firstNameState = atom({ key: 'firstName', default: '' });
const lastNameState = atom({ key: 'lastName', default: '' });
const fullNameState = selector({ key: 'fullName', get: ({ get }) => {
  return `${get(firstNameState)} ${get(lastNameState)}`;
}});
Combines multiple atoms into a derived state (full name) using a selector.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Duplicate key error
Ensure that every atom or selector has a unique `key` string.
Components not updating
Check that you are using Recoil hooks (`useRecoilState`, `useRecoilValue`) and not React state for the same value.
Selector fetch fails
Handle errors in async selectors using try/catch and consider fallback states.

Best practices

  • Use atoms for shared state and selectors for derived or computed state.
  • Keep atoms small and focused to avoid unnecessary re-renders.
  • Use unique `key` values for all atoms and selectors.
  • Leverage asynchronous selectors for data fetching and caching.
  • Avoid excessive use of global atoms; local component state is sometimes simpler.

Background

Why it exists, and what it was reacting to.

Recoil was developed by Facebook to address complex state management needs in React applications. It introduces atoms (pieces of state) and selectors (derived or computed state), providing a more declarative and scalable approach compared to prop drilling or using large global stores.