Redux is too much structure for simple state. Context API re-renders everything. Zustand is great but uses a single store. What if you wanted state that's as simple as useState but shared across components?
That's Jotai — atomic state management inspired by Recoil, but simpler.
The Core Concept: Atoms
import { atom, useAtom } from "jotai";
// Create an atom — like a global useState
const countAtom = atom(0);
const nameAtom = atom("Aleksej");
function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
function NameDisplay() {
const [name] = useAtom(nameAtom);
return <p>Hello, {name}</p>;
}
// Both components use global state — no Provider required (with default store)
Derived Atoms
const priceAtom = atom(100);
const quantityAtom = atom(1);
const taxRateAtom = atom(0.2);
// Read-only derived atom — recomputes when dependencies change
const totalAtom = atom((get) => {
const price = get(priceAtom);
const quantity = get(quantityAtom);
const tax = get(taxRateAtom);
return price * quantity * (1 + tax);
});
// Read-write derived atom
const celsiusAtom = atom(0);
const fahrenheitAtom = atom(
(get) => get(celsiusAtom) * 9/5 + 32,
(get, set, newF: number) => set(celsiusAtom, (newF - 32) * 5/9)
);
Async Atoms
const userIdAtom = atom(1);
const userAtom = atom(async (get) => {
const id = get(userIdAtom);
const res = await fetch(`/api/users/${id}`);
return res.json();
});
function UserProfile() {
const [user] = useAtom(userAtom); // Suspense-ready!
return <h1>{user.name}</h1>;
}
// Wrap with Suspense
<Suspense fallback={<Loading />}>
<UserProfile />
</Suspense>
Jotai vs Zustand vs Recoil
| Feature | Jotai | Zustand | Recoil |
|---|---|---|---|
| Model | Atoms (bottom-up) | Store (top-down) | Atoms |
| Re-renders | Per-atom | Per-selector | Per-atom |
| Async | Built-in (Suspense) | Manual | Built-in |
| Bundle size | 2 KB | 1 KB | 20 KB |
| Provider | Optional | Not needed | Required |
| Maintained | Active | Active | Deprecated |
Choose Jotai when state is naturally split into independent pieces (form fields, toggles, filters). Choose Zustand when state is a single coherent object (shopping cart, user session).
Start here: jotai.org
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)