How to Use Jotai for Atomic State in React

Set up Jotai in React. Create atoms with the atom function, read and update them with useAtom, and build derived values that stay in sync.

6 min read

Jotai is a primitive and flexible state management library for React built around atoms. Each atom holds one piece of state, and any component can read or update it with a hook. Atoms can also derive from other atoms, so one value stays in sync with another automatically.

Install Jotai

Jotai ships as a single package with a tiny API. Install it in any React project, including Vite and Next.js.

bashbash
npm install jotai

The core API is two functions: atom defines a value and useAtom reads and updates it. Basic usage needs no provider, so there is nothing to wrap at the root.

Create and read an atom

An atom starts with an initial value. useAtom returns the current value and a setter, much like useState, but the atom is shared across the whole app.

App.jsxApp.jsx
import { atom, useAtom } from "jotai";
 
const countAtom = atom(0);
 
export function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
      <span>{count}</span>
    </div>
  );
}

Clicking the button increments the atom, and every component that reads countAtom re-renders with the new value. The setter accepts a function, which is the safe way to update based on the previous value. The atom object itself is stable, so passing it around never causes a re-render by itself.

Atoms vs component state

Component state stays inside one component. An atom lives outside the component tree, so two components that read the same atom always see the same value.

Think of an atom as a small, single value store. Where useState keeps a value local, an atom keeps it shared without any context or prop setup. That makes atoms a good middle step between lifting state and adopting a full store.

Share one atom across components

Because the atom is defined once and imported, two components stay in sync without lifting state or passing props.

App.jsxApp.jsx
function Display() {
  const [count] = useAtom(countAtom);
  return <p>Current count: {count}</p>;
}

When Counter increments the atom, Display updates too. There is no prop drilling and no context wrapper, because the atom is the shared source of truth.

Update an atom from anywhere

The setter returned by useAtom is the same no matter which component calls it. A button deep in the tree can update an atom that a header reads, with no callback prop passed between them.

Derive values from atoms

A derived atom reads other atoms with get and computes a value from them. It has no setter of its own.

App.jsxApp.jsx
const doubledAtom = atom((get) => get(countAtom) * 2);
 
function Doubled() {
  const [doubled] = useAtom(doubledAtom);
  return <p>Doubled: {doubled}</p>;
}

When countAtom changes, doubledAtom recomputes, and Doubled re-renders with the new value. Jotai tracks the dependency automatically, so you never update a derived value by hand. Derived atoms stay read-only, which keeps the data flow one directional and easy to follow.

Write to multiple atoms

A writable derived atom can update several atoms in one call. Its write function receives get and set, plus any arguments the caller passes.

App.jsxApp.jsx
const priceAtom = atom(10);
const discountedAtom = atom(
  (get) => get(priceAtom),
  (get, set, discount) => set(priceAtom, get(priceAtom) - discount)
);

The second argument to atom is the write function, called when a component sets discountedAtom. It reads the current price and writes the reduced value back. This is how one action can update several related atoms in a single place.

When Jotai is the right fit

Jotai fits fine grained state where many small values derive from each other. It also shines for derived values that change as other values change, like a cart total.

It is less useful for one big store with a rigid shape, where Redux Toolkit or Zustand may fit better. A broader comparison is in the state management decision guide.

The split between client and server data is separate from this choice. Jotai manages client state, while server cache belongs in a data library, as client state vs server state in React explains.

For values that never leave one component, see how to avoid global state when local state is enough. For most small apps, a few atoms are all the shared state you need.

Rune AI

Rune AI

Key Insights

  • Install jotai and import atom and useAtom.
  • Create a shared value with atom and an initial value.
  • Read and update it with useAtom in any component.
  • Build derived atoms with a read function that uses get.
RunePowered by Rune AI

Frequently Asked Questions

Does Jotai need a Provider?

No, not for basic usage. Atoms hold their values in a default store, so any component can read them without wrapping the app.

What is the difference between an atom and useState?

useState keeps a value inside one component. An atom lives outside components, so many components can read and update the same value.

What is a derived atom?

An atom created with a read function that uses get to read other atoms. It recomputes automatically when a dependency changes.

Conclusion

Jotai models state as small atoms. Create an atom with atom, read and update it with useAtom, and build derived atoms that recompute from their dependencies.