
Jotai: State That Only Wakes Who Needs It
Bottom-up atoms instead of a single store. Derived state is a computed atom, and only the components reading a changed atom re-render.
Zustand and Jotai are usually presented as alternatives, but they answer different questions. Zustand asks what is the shape of my app's state. Jotai asks what is the smallest independent piece of state, and what derives from it.
An atom is the unit
import { atom, useAtom } from 'jotai'
export const queryAtom = atom('')
export const tagsAtom = atom([])
function SearchBox() {
const [query, setQuery] = useAtom(queryAtom)
return <input value={query} onChange={e => setQuery(e.target.value)} />
}
No provider needed for the default store, no selector, and no reducer. SearchBox subscribes to queryAtom and nothing else — a change to tagsAtom cannot re-render it, because it never touched it.
Derived state is the actual feature
export const resultsAtom = atom((get) => {
const q = get(queryAtom).toLowerCase()
const tags = get(tagsAtom)
return allPosts.filter(p =>
p.title.toLowerCase().includes(q) &&
tags.every(t => p.tags.includes(t))
)
})
resultsAtom recomputes when either input changes, and nothing else does. In a store-based design this is a selector you have to remember to memoise correctly; here the dependency graph is the code, so it cannot drift from what the computation actually reads.
Async derivations work the same way, suspending rather than needing a separate loading flag:
export const userAtom = atom(async (get) => {
const id = get(userIdAtom)
return id ? (await fetch(`/api/users/${id}`)).json() : null
})
Where it beats a single store
Editors, builders, dashboards — anywhere many small values change rapidly and independently. A canvas editor where dragging updates one shape's position should not re-render a panel showing a different shape. With atoms that is the default rather than something you tune later with memo and selectors.
The cost
Discipline. Atoms are so cheap to create that they end up scattered across twenty files, and six months later nobody can answer "what is the state of this screen". Keep them in a small number of modules per feature and export them deliberately.
How I choose
One coherent domain with a handful of shared values: Zustand, because a reader can see all of it at once. Many fine-grained values with real derivation chains: Jotai, because that is exactly the shape it was designed for. And in both cases, server data goes to the query layer, not into either one.
Resources
- Repo: pmndrs/jotai
- Docs: jotai.org
- Video walkthroughs: YouTube: jotai react state tutorial
- Related: Zustand: state you can read in one sitting
Need this built properly?
I build secure, fast, bilingual platforms for clients across Egypt, Saudi Arabia, the UAE and Kuwait.


