Jotai: Atomic State for React, From useState Replacement to v3.0.0
đź‘» Primitive and flexible state management for React
At a glance
- What is it?
- Jotai replaces a single global store with small primitive atoms that components subscribe to individually. It installs with one npm command and its core API is small, but async atoms require Suspense and the package now requires TypeScript 5.5 or newer.
- Who is it for?
- Adopt Jotai when your React state is spread across many independent values and you want components to subscribe to individual atoms instead of one store, and when you can run TypeScript 5.5 or newer. Skip it if you need a framework-agnostic store shared with non-React code, since the documented entry points here are React and vanilla, or if Suspense-based async reads do not fit your rendering model.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Jotai solves, and who it is aimed at
The README frames Jotai as scaling "from a simple useState replacement to an enterprise TypeScript application," and that sentence is the clearest statement of intent in the repository. The package description is "Primitive and flexible state management for React." The problem it addresses is the shape of shared state in a React tree. Instead of one store object that components select slices from, Jotai asks you to define atoms: small units of state, each with its own value, that any component can read or write with a hook. The README contrasts this with Recoil by noting the absence of string keys. Atoms are values you import, not identifiers you look up by name at runtime, so a typo becomes a compile error rather than a silently undefined selector.
The audience is React developers who already accept hooks and want state to be composed rather than centralized. The repository ships examples named hello, todos, todos_with_atomFamily, mega-form, text_length, hacker_news and starter, which suggests the maintainers expect people to arrive through a small demo and grow from there. The core is described as 2kb, and the package.json marks the package as "sideEffects": false, which matters to bundlers doing tree shaking. If your state is one counter and one form field, this is more machinery than you need. The value appears when many components need many independent pieces of state, and when derived values should update without a manual memo layer.
How atoms, reads and writes actually work
An atom is created by calling atom() with an initial value. That value can be a string, a number, an object or an array, and the README shows four primitive atoms created this way, including one holding a manga publication-year object. Reading happens through useAtom in a component, which returns a tuple shaped like useState: the current value and a setter. The README's Counter example increments with a functional update, setCount((c) => c + 1), so the setter accepts the same updater form React developers already know.
Derived atoms are where the model diverges from a plain store. Passing a read function as the first argument produces a read-only atom, and the get parameter inside that function fetches the current value of any other atom. The README's doubledCountAtom is atom((get) => get(countAtom) * 2). Combining atoms works the same way: a sum atom calls get on three separate atoms and adds them, and the README also shows mapping get over an array of atoms and reducing the result, which is the pattern you want when the set of inputs is dynamic.
Writable derived atoms take a second argument, a write function receiving get and set. The decrementCountAtom example reads countAtom and writes countAtom minus one. Omitting the read function entirely gives a write-only atom: multiplyCountAtom passes null as the first argument and uses set inside the second, so the component destructures only the action and ignores the value. Async reads are supported by making the read function async; the README marks this path as needing Suspense, and the fetchUrlAtom example awaits fetch before returning JSON. Async writes work too, with the write function awaiting a response and then calling set. The README closes with a note that atoms are monads "just like promises," linking to a functional-programming page on the documentation site. That is a design claim rather than a performance claim, and it is worth reading before you decide whether the composition style suits your team.
Installing Jotai and writing a first atom
The README gives exactly one install instruction: npm i jotai. The documentation site is jotai.org, and the repository's examples directory is the place to look for runnable starting points. The package exposes several entry points, so the import path you choose matters. The root export is jotai; there are also jotai/utils, jotai/react, jotai/react/utils, and vanilla variants including jotai/vanilla/internals.
A minimal first use is a primitive atom plus a component that reads and writes it. This is the shape the README demonstrates, with the atom defined at module scope so it is not recreated on every render.
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}Once the primitive works, add a derived atom. The read function receives get, and the component re-renders when the underlying atom changes.
const doubledCountAtom = atom((get) => get(countAtom) * 2)
function DoubleCounter() {
const [doubledCount] = useAtom(doubledCountAtom)
return <h2>{doubledCount}</h2>
}If you try an async read atom, expect to wrap the consuming component in a Suspense boundary, because the README labels that feature as needing Suspense. Nothing in the README documents a fallback path for async reads without it.
The TypeScript 5.5 gate and other sharp edges
The most concrete constraint in the repository is the TypeScript version requirement. The package.json typesVersions field maps any compiler below 5.5 to a single declaration file named ts_version_5.5_and_above_is_required.d.ts. Every export condition repeats this pattern: types@>=5.5 points at the real declarations, and the plain types condition points at the same rejection file. In practice, a project on TypeScript 5.4 or older will not get useful types from Jotai at all. The failure is deliberate and legible, but it is a hard floor, and monorepos with mixed compiler versions will hit it selectively.
Suspense is the second constraint, and it is a design choice rather than a bug. Async read atoms suspend, so any component reading one must sit under a Suspense boundary. Teams that avoid Suspense for data fetching, or that render on a path where a suspended component has no boundary above it, will find this model awkward. The README does not present an alternative for async reads.
The third edge is conceptual. Atoms are monads according to the README, and the composition style follows from that. Developers who have not worked with that pattern may find derived atoms harder to reason about than a selector function over a store, particularly when writes are involved. The README itself links to a dedicated functional-programming page, which is a signal that the maintainers consider it prerequisite reading rather than trivia. None of this makes Jotai the wrong tool, but it does mean the learning curve is not zero, and the 2kb core size says nothing about the time your team spends understanding it.
Jotai versus Zustand and Redux: different centers of gravity
Zustand appears in the related searches alongside Jotai, and the difference is structural. A Zustand-style store is a single object holding state and actions, and components subscribe to a selected slice of it. Jotai has no store object you author; the README's atom model means state is distributed across as many atoms as you define, and the store is something the library manages underneath. The practical consequence is granularity. In Jotai, a component that reads one atom re-renders on that atom's changes without you writing a selector; in a single-store model, you write the selector and manage its equality behavior yourself. The trade is that Jotai spreads state definitions across files, and finding every writer of a given atom means searching for imports rather than reading one reducer.
Redux is the other comparison the README invites by mentioning Recoil and string keys. Redux centralizes state, actions and reducers, with devtools and middleware as first-class parts of the ecosystem. Jotai's README describes a minimal core with utilities and extensions layered on top. If your team depends on action logs, time-travel debugging or middleware for side effects, a centralized store is the natural fit, and Jotai's atom graph is a different mental model to adopt. If your pain is that a single store forces unrelated components to re-render or forces you to write selectors for everything, the atom model addresses that directly. Neither is universally better; they optimize for different failure modes.
Maintenance, licensing and what an upgrade costs
The repository is not archived, and the last push was on 2026-09-17, four days before this writing. The most recent release is v3.0.0, published on 2026-09-08, preceded by v3.0.0-alpha.1 on 2026-08-24 and v2.20.3 on the same day. That sequence matters: the jump from v2.20.3 to a v3.0.0 major release means a breaking-change boundary exists, and the repository does not enumerate what changed. Anyone upgrading from the 2.x line should read the release notes rather than assume compatibility.
The license is MIT, which permits commercial and private use with the usual attribution and warranty conditions. That is a permissive, well-understood license, and nothing in the repository suggests dual licensing or a commercial tier. This is not legal advice, and if your organization has a license review process, the LICENSE file at the repository root is the authoritative text.
Upgrade cost is where the package.json details become practical. The typesVersions gate means every upgrade should be checked against your TypeScript version first; a compiler bump may be a prerequisite rather than a follow-up. The multiple export paths (jotai, jotai/utils, jotai/react, jotai/react/utils, and the vanilla entries) mean an upgrade can affect different parts of your codebase independently, so a single import-path audit before upgrading is cheaper than discovering the split after. The repository also carries benchmarks, tests and a vitest.config.mts, which tells you the maintainers run a test suite, though the repository does not report what it covers.
Editorial conclusion
Adopt Jotai when your React state is spread across many independent values and you want components to subscribe to individual atoms instead of one store, and when you can run TypeScript 5.5 or newer. Skip it if you need a framework-agnostic store shared with non-React code, since the documented entry points here are React and vanilla, or if Suspense-based async reads do not fit your rendering model. Before committing, check that your TypeScript version satisfies the typesVersions gate, confirm which entry point you import from (jotai, jotai/utils, jotai/react, or the vanilla paths), and read the v3.0.0 release notes for anything that changed since v2.20.3.
Frequently asked questions
What is Jotai for?
Jotai is primitive and flexible state management for React. It lets you define small atoms that hold values and derive new atoms from them, then read and write those atoms in components with a hook shaped like useState.
How do I install Jotai?
The README gives one command, npm i jotai. The documentation site is jotai.org, and the repository's examples directory contains runnable starting points.
How do I use Jotai in React?
Create an atom with atom() and an initial value, then call useAtom in a component to get the value and a setter. Derived atoms take a read function whose get parameter reads other atoms, and writable derived atoms take a second write function receiving get and set.
What is a Jotai atom?
An atom represents a piece of state. You create one by specifying an initial value, which can be a primitive, an object or an array, and you can create as many primitive atoms as you want.
What is the difference between Jotai and Zustand?
Jotai has no author-defined store object; state is distributed across atoms, and components read individual atoms without writing selectors. A Zustand-style store is a single object whose slices components subscribe to, so the two differ in where state is defined and how subscriptions are expressed.
Is Jotai better than Redux?
They optimize for different things. Redux centralizes state, actions and reducers with devtools and middleware as first-class parts of the ecosystem, while Jotai distributes state across atoms and re-renders only the components reading a changed atom. The README notes that Jotai has no string keys, unlike Recoil.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pmndrs-jotai)