XState Store
Targets @xstate/store v4 and @xstate/store-react v2 (TypeScript 5.4+). Read the reference matched by the API or behavior being changed. Load adjacent references only for concerns the change crosses; reuse relevant context already read.
Pick the primitive first
| State |
Primitive |
Why |
| Domain state updated through named events |
createStore({ context, on }) |
Typed trigger, can, emitted events, replayable transitions |
| Per-instance state built from input |
createStoreLogic({ context: (input) => … }) |
One definition, many instances; selectors come along |
| A single value set directly |
createAtom(value) |
No event vocabulary to justify a store |
| A value computed from other atoms or selectors |
createAtom(() => …) |
Read-only, recomputes on dependency change |
| Modes, guards, delays, hierarchical or parallel states |
xstate machine |
A store models data, not lifecycle |
Done when: the primitive is chosen from this table and its justification holds.
Branches
| When you are… |
Reference |
Creating a store, writing transitions, using trigger/send/can, or Immer |
references/store-core.md |
| Enqueuing effects, emitting events, or doing async work |
references/effects-and-events.md |
Declaring schemas or turning on runtime validation |
references/schemas-and-validation.md |
Reading state with store.select, atoms, derived/async/reducer atoms |
references/selectors-and-atoms.md |
Building reusable/per-instance stores with createStoreLogic, input, or selectors |
references/store-logic-and-input.md |
Adding persist, undoRedo, reset, or composing .with(...) |
references/extensions.md |
| Wiring a store into React components |
references/react.md |
| Testing transitions, inspecting a store, or interoperating with XState |
references/testing-and-interop.md |
| Upgrading a v3 store to v4, or porting an existing Zustand store over |
references/migration-v4.md |
Apply the technical rules relevant to the changed behavior.
Tripwires
Transitions — return the complete next context (spread the old one); return undefined to mark an event disallowed, which is what store.can.*() reports.
Effects — call enqueue.effect, enqueue.emit.*, and enqueue.trigger.* synchronously inside the transition; async work goes inside enqueue.effect(async () => …) and reports back by triggering another event.
Imports — framework bindings come from @xstate/store-react (and siblings), never @xstate/store/react; extensions come from @xstate/store/persist, /undo, /reset, /validate.
Schemas — schemas types the store; only .with(validateSchemas()) validates at runtime.
Atoms — computed atoms read dependencies via .get(); their first parameter is the previous computed value, so annotate createAtom<T>(…) when using it.
React — module-scoped stores for app-wide state, useStore(logic, input) for component-scoped state; subscribe through useSelector/useAtom rather than getSnapshot().
1---2name: xstate-store3description: Build, test, integrate, or migrate XState Store v4: transitions, effects, schemas, selectors, atoms, extensions, React bindings, and fromStore. Excludes XState state-machine design, other stores, and Query server state.4---56# XState Store78Targets `@xstate/store` v4 and `@xstate/store-react` v2 (TypeScript 5.4+). Read the reference matched by the API or behavior being changed. Load adjacent references only for concerns the change crosses; reuse relevant context already read.910## Pick the primitive first1112| State | Primitive | Why |13| --- | --- | --- |14| Domain state updated through named events | `createStore({ context, on })` | Typed `trigger`, `can`, emitted events, replayable transitions |15| Per-instance state built from input | `createStoreLogic({ context: (input) => … })` | One definition, many instances; `selectors` come along |16| A single value set directly | `createAtom(value)` | No event vocabulary to justify a store |17| A value computed from other atoms or selectors | `createAtom(() => …)` | Read-only, recomputes on dependency change |18| Modes, guards, delays, hierarchical or parallel states | `xstate` machine | A store models data, not lifecycle |1920*Done when:* the primitive is chosen from this table and its justification holds.2122## Branches2324| When you are… | Reference |25| --- | --- |26| Creating a store, writing transitions, using `trigger`/`send`/`can`, or Immer | `references/store-core.md` |27| Enqueuing effects, emitting events, or doing async work | `references/effects-and-events.md` |28| Declaring `schemas` or turning on runtime validation | `references/schemas-and-validation.md` |29| Reading state with `store.select`, atoms, derived/async/reducer atoms | `references/selectors-and-atoms.md` |30| Building reusable/per-instance stores with `createStoreLogic`, input, or `selectors` | `references/store-logic-and-input.md` |31| Adding `persist`, `undoRedo`, `reset`, or composing `.with(...)` | `references/extensions.md` |32| Wiring a store into React components | `references/react.md` |33| Testing transitions, inspecting a store, or interoperating with XState | `references/testing-and-interop.md` |34| Upgrading a v3 store to v4, or porting an existing Zustand store over | `references/migration-v4.md` |3536Apply the technical rules relevant to the changed behavior.3738## Tripwires3940**Transitions** — return the complete next context (spread the old one); return `undefined` to mark an event disallowed, which is what `store.can.*()` reports.4142**Effects** — call `enqueue.effect`, `enqueue.emit.*`, and `enqueue.trigger.*` synchronously inside the transition; async work goes inside `enqueue.effect(async () => …)` and reports back by triggering another event.4344**Imports** — framework bindings come from `@xstate/store-react` (and siblings), never `@xstate/store/react`; extensions come from `@xstate/store/persist`, `/undo`, `/reset`, `/validate`.4546**Schemas** — `schemas` types the store; only `.with(validateSchemas())` validates at runtime.4748**Atoms** — computed atoms read dependencies via `.get()`; their first parameter is the previous computed value, so annotate `createAtom<T>(…)` when using it.4950**React** — module-scoped stores for app-wide state, `useStore(logic, input)` for component-scoped state; subscribe through `useSelector`/`useAtom` rather than `getSnapshot()`.