RepoPilot

How do their APIs compare?

About pmndrs/jotai · 2 messages · last activity 8/8/2026

Question

How do their APIs compare?

RepoPilot

Here's a comparison of the two libraries' APIs, grounded in what's shown in the repository data and the retrieved source:

pmndrs/jotai — atom-based, primitive-first

Jotai's core primitive is the atom, created with atom(). Per the README excerpt, an atom represents a single piece of state and can hold primitives, objects, or arrays:

const countAtom = atom(0)
const countryAtom = atom('Japan')

Derived (computed) atoms are created by passing a read function that receives a get accessor to compose other atoms:

const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())

This pattern is shown concretely in the example app [examples/hello/src/App.tsx:L21-L32], where uppercaseAtom derives from textAtom.

Consumption in React is via the useAtom hook, styled deliberately like useState:

const [count, setCount] = useAtom(countAtom)

seen both in the README and in [examples/hello/src/App.tsx:L38-L43] (useAtom(uppercaseAtom) returning a read-only value).

Key API characteristics:

  • No string keys — atoms are just JS references, unlike Recoil (explicitly called out in the README).
  • Minimal core (~2kb), with additional functionality split into subpaths: jotai/vanilla, jotai/utils, jotai/react, jotai/vanilla/utils, jotai/react/utils, visible in the import map in [.livecodes/react.json:L1-L28].
  • The internal architecture (per RepoPilot's import graph) centers on src/vanilla.ts (22 importers), src/vanilla/atom.ts (11 importers), and src/vanilla/store.ts (9 importers) — meaning atoms and a vanilla (framework-agnostic) store are the load-bearing abstractions that the React bindings sit on top of.

statelyai/xstate — state machine / actor model

xstate's API is structurally different — it's built around state machines/statecharts and, in the retrieved React bindings, an actor model:

  • createMachine defines a machine from a config object with states, context, on (transitions), invoke, entry/exit actions, etc. This schema is formalized in [packages/core/src/machine.schema.json:L36-L75] (compound state nodes) and [packages/core/src/machine.schema.json:L106-L145] (atomic state nodes), showing structured concepts like initial, transitions, invoke, entry/exit actions that have no equivalent in jotai's atom model.
  • React integration (packages/xstate-react) exposes hooks like useActor, useActorRef, useSelector, and createActorContext, per the barrel file [packages/xstate-react/src/index.ts:L1-L9]. useMachine is explicitly marked deprecated in favor of the actor-based hooks.
  • useSelector supports a comparison function to avoid unnecessary re-renders, defaulting to reference equality [packages/xstate-react/src/useSelector.ts:L9-L12], and there's a dedicated shallowEqual utility for deep-ish comparisons of selected values [packages/xstate-react/src/shallowEqual.ts:L10-L38].
  • Testing/graph utilities like createTestModel and getShortestPaths operate over machine definitions to generate paths through the statechart, as seen in [packages/core/src/graph/types.test.ts:L1-L40] — this reflects xstate's emphasis on modeling behavior/transitions explicitly, which jotai has no analogue for since it models data dependencies, not state transitions.

High-level contrast

Aspectjotaixstate
Core unitatom (a value + optional derivation function)machine (states, transitions, context)
Compositionget/set inside atom read/write functionsevents (on), transitions, invoked actors
React bindinguseAtom (useState-like)useActor/useActorRef/useSelector (actor-model-like)
Equality/render controlImplicit via atom identity/value diffingExplicit via useSelector compare fn or shallowEqual
Mental modelBottom-up

Want to ask your own question?

Open chat for pmndrs/jotai

Public Q&A. Generated by RepoPilot from the actual source of pmndrs/jotai. AI answers can be incomplete or stale — verify before relying on them.