Implement, refactor, and review XState v5 state machines in TypeScript with strict setup().createMachine() ruleset, design-first planning, params-first typing, anti-god-machine enforcement, and canonical actor patterns (fromPromise, fromCallback, fromObservable, fromTransition). Covers invoke vs spawnChild boundaries, hierarchical/parallel states, delayed transitions, cleanup patterns, and real-world patterns like SDK bridging with fromCallback actors and Promise-bridge for async external events. Use when building state machines, actors, statecharts, finite state logic, actor systems, or reviewing/refactoring XState code.
Resources
1Install
npx skillscat add aiocean/claude-plugins/aio-xstate Install via the SkillsCat registry.
XState v5 Strict Skill
XState v5 ONLY. Requires TypeScript 5.0+. Never use v4 patterns.
Workflow
- Design first (unless user says skip): produce planning artifacts before code
- Types first:
types.context,types.events,types.input(+types.output) - Implement:
setup({...}).createMachine({...})with all implementations in setup - Validate:
tsc --noEmit, noanyleaks, no string events, no v4 imports
Quick Reference
import { setup, assign, fromPromise, createActor } from "xstate"
const machine = setup({
types: {
context: {} as { count: number },
events: {} as { type: "inc" } | { type: "add"; amount: number },
input: {} as { initialCount?: number },
},
actions: {
inc: assign({ count: ({ context }) => context.count + 1 }),
add: assign({
count: ({ context }, params: { amount: number }) => context.count + params.amount,
}),
},
guards: {
isPositive: ({ context }) => context.count > 0,
},
}).createMachine({
id: "counter",
context: ({ input }) => ({ count: input.initialCount ?? 0 }),
initial: "active",
states: {
active: {
on: {
inc: { actions: "inc" },
add: {
actions: {
type: "add",
params: ({ event }) => ({ amount: event.amount }),
},
},
},
},
},
})
const actor = createActor(machine, { input: { initialCount: 0 } })
actor.subscribe((snapshot) => console.log(snapshot.context.count))
actor.start()
actor.send({ type: "inc" })Hard Rules
setup({...}).createMachine({...})for all machines- All implementations in
setup(): actions, guards, actors - Typed params: actions/guards take
(_, params: { ... })second arg - Event objects only:
actor.send({ type: "X" }), never strings - No god-machine: unrelated domains = separate actors/machines
- No state-mirroring booleans: use state nodes for modes, context for data
invokehasonError: always handle failure- No v4 patterns: no
interpret,Machine,cond,send,pure,choose
v4 to v5 Migration
| v4 (WRONG) | v5 (CORRECT) |
|---|---|
createMachine() alone |
setup().createMachine() |
interpret() |
createActor() |
services: {} |
actors: {} |
cond |
guard |
send() action |
raise() or sendTo() |
pure()/choose() |
enqueueActions() |
withContext() |
input |
withConfig() |
provide() |
spawn import |
spawnChild or ({ spawn }) args |
Actor Types
| Type | Creator | Use Case |
|---|---|---|
| State Machine | setup().createMachine() |
Complex state logic |
| Promise | fromPromise() |
Async request/response |
| Callback | fromCallback() |
Bidirectional streams, SDK bridging |
| Observable | fromObservable() |
RxJS streams |
| Transition | fromTransition() |
Reducer-like state |
invoke vs spawnChild
- invoke: actor lifecycle tied to state. Created on entry, stopped on exit. Use for request/response
- spawnChild: dynamic actors independent of state. Use for long-lived collaborators with
sendTo
Planning Artifacts (Design-First)
Before writing machine code, produce:
- Goal: one sentence
- Non-goals: what the machine does NOT handle
- State inventory: name, meaning, invariants per state
- Event catalog: name, payload, source (UI/network/timer/child)
- Transition table:
from -> on event -> guard? -> actions -> to - Async/actor map: invoke vs spawnChild, onDone/onError, cancellation
- Error strategy: state vs context, retry policy, fatal vs recoverable
Detailed References
- rules.md — Complete enforcement rules, forbidden patterns, testing, React integration
- patterns.md — Actor patterns: fromCallback, fromPromise, hierarchy, parallel, delays, cleanup, SDK bridging, Promise-bridge