
Jotai vs Recoil 2026: The Atomic State Migration (Recoil Is Deprecated)
Recoil is deprecated by Meta. Jotai is the active successor. API comparison, migration cheatsheet, RSC compatibility, and bundle numbers.
If you started a Recoil project between 2020 and 2023, this post is for you. Meta archived the Recoil repository, and the project no longer ships meaningful releases. Recoil is deprecated — do not start new projects with it. Jotai is the actively maintained spiritual successor with the same atomic mental model and better React 19 / Next.js 16 RSC compatibility.
For the wider state-management landscape, see our complete React state management guide. For when atomic state is even the right tool, read server state vs client state in React 2026 first — much of what people reach for atoms for is actually server state.
Why this comparison matters now
Recoil's last meaningful release was years ago. It still installs, it still works in React 18, but it has unresolved React 19 issues, no concurrent-rendering refinements, and Meta has publicly moved on. Choosing it for new code in 2026 is choosing to inherit a migration debt later.
Jotai (by Daishi Kato, also author of Zustand and Valtio) covers the same atomic ground: small primitive units of state, derived atoms, fine-grained subscriptions, no reducers required. It is actively maintained and has explicit React 19 / Suspense support.
API similarity, side by side
// Recoil
import { atom, selector, useRecoilState, useRecoilValue } from 'recoil'
const countAtom = atom({ key: 'count', default: 0 })
const doubledSelector = selector({
key: 'doubled',
get: ({ get }) => get(countAtom) * 2,
})
function Counter() {
const [count, setCount] = useRecoilState(countAtom)
const doubled = useRecoilValue(doubledSelector)
return <button onClick={() => setCount(count + 1)}>{count} ({doubled})</button>
}
// Jotai
import { atom, useAtom, useAtomValue } from 'jotai'
const countAtom = atom(0)
const doubledAtom = atom((get) => get(countAtom) * 2)
function Counter() {
const [count, setCount] = useAtom(countAtom)
const doubled = useAtomValue(doubledAtom)
return <button onClick={() => setCount(count + 1)}>{count} ({doubled})</button>
}
Same model. Jotai removes the key string requirement (it uses object identity), drops the artificial atom vs selector split, and ships smaller. Anyone fluent in Recoil is productive in Jotai inside an hour.
What Jotai improved
- No string keys. Atom identity is the JS reference. No more "duplicate atom key" runtime errors.
- Unified primitive. Read-only, write-only, and derived atoms are all just
atom(...). - Async atoms with Suspense. An async
atomgetter integrates cleanly with<Suspense>boundaries in React 19. - Smaller bundle. Jotai core is roughly half the size of Recoil.
- Active ecosystem.
jotai/utils,jotai-tanstack-query,jotai-zustand, devtools — actually shipping. - Better RSC story.
jotai/reactworks in Next.js 16 App Router with documented per-request store patterns.
What Recoil had that Jotai lacks
Honest accounting: Recoil's atomFamily and selectorFamily were elegant for parametric atoms. Jotai's atomFamily from jotai/utils covers the same ground but with slightly different semantics around equality and cleanup. Recoil's snapshot/transaction APIs (useRecoilCallback, snapshots) had no exact 1:1 in Jotai for a while; useAtomCallback and the imperative store API close most of that gap in 2026.
Migration cheatsheet
atom({ key: 'x', default: v })→atom(v)selector({ key: 'd', get: ({get}) => ... })→atom((get) => ...)useRecoilState(a)→useAtom(a)useRecoilValue(a)→useAtomValue(a)useSetRecoilState(a)→useSetAtom(a)atomFamily({ key, default })→atomFamily((param) => atom(...))fromjotai/utils<RecoilRoot>→<Provider>fromjotai(or omit for the default global store)
For a typical Recoil codebase, a focused codemod plus a half-day cleanup pass per medium app is realistic. Atom families and snapshot-heavy code take longer.
RSC and Suspense compatibility
In Next.js 16 App Router, atoms only live in Client Components. Wrap the client tree once:
// app/providers.tsx
'use client'
import { Provider, createStore } from 'jotai'
import { useState, type ReactNode } from 'react'
export function JotaiProvider({ children }: { children: ReactNode }) {
const [store] = useState(() => createStore())
return <Provider store={store}>{children}</Provider>
}
Async atoms suspend correctly inside React 19's <Suspense> boundaries. Recoil's async story still produces stale-snapshot warnings under React 19's stricter concurrent semantics in many real codebases.
Bundle size
- Jotai core: ~3-4kb gzipped.
- Recoil: ~14-16kb gzipped (and frozen in time).
For a single-page app where atomic state is the core architecture, that delta compounds across every route.
Verdict
If you have new work, pick Jotai. If you have an existing Recoil app, plan the migration before React 20 — leaving a deprecated dependency in a critical path is a debt that only compounds. The API is similar enough that the rewrite is mostly mechanical, and you finish with a smaller, faster, actively maintained codebase.
FAQ
Is Recoil officially deprecated?
The Recoil repository was archived by Meta in early 2025 and the project no longer receives meaningful updates. The community has accepted Jotai as the successor for atomic state in React.
Can Jotai replace Recoil 1:1?
For 90% of patterns, yes — including atoms, selectors, atom families, async, and writable derived state. Snapshot-heavy code (useRecoilCallback with full snapshots) needs more thought.
Does Jotai work with React Server Components?
Yes, on the client side. Atoms only live in Client Components. Use a per-request createStore() in Next.js App Router to avoid cross-request state leaks.
Is Jotai smaller than Zustand?
They are in the same weight class, low single-digit kilobytes gzipped. Choose based on the mental model: atoms (Jotai) vs single store with selectors (Zustand). Both are excellent.
Should I pick Jotai over TanStack Query?
They solve different problems. Jotai is for client state (UI, derived values). TanStack Query is for server state (cache, refetch, invalidation). Use both.
Get weekly highlights
No spam, unsubscribe anytime.
Cal.com
Open source scheduling — self-host your booking system, replace Calendly. Free & privacy-first.
Fillout
Powerful form builder for devs — integrates with Next.js, React, Notion, Airtable. Save hours of coding.



Comments (0)
Sign in to comment
No comments yet. Be the first to comment!