XState Machine Definition
Define statecharts with createMachine for explicit states, transitions, context, and events
When to Use
- Modeling complex UI flows with multiple states (forms, wizards, authentication)
- Replacing boolean flag soup (
isLoading && !isError && isSubmitted) with named states - Building workflows where illegal state transitions must be prevented
- Any feature where a state diagram would clarify the logic
Instructions
- Define the machine in a
.machine.tsfile. Keep it separate from UI components. - Start with the types: define
Context(extended state data) andEvents(a discriminated union of all possible events). - Use
createMachinewithid,initial,context, andstates. Each state hasonfor transitions. - Name states as nouns or adjectives (
idle,loading,error), not verbs. - Name events as past-tense descriptions of what happened (
SUBMITTED,LOADED,ERRORED) or imperative commands (SUBMIT,RETRY). - Use
targetfor the destination state. Omittargetfor self-transitions that only run actions. - Keep machines pure — no side effects in the machine definition itself. Use actions and services for effects.
// auth.machine.ts
import { createMachine, assign } from 'xstate';
interface AuthContext {
user: { id: string; name: string } | null;
error: string | null;
retries: number;
}
type AuthEvent =
| { type: 'LOGIN'; email: string; password: string }
| { type: 'LOGOUT' }
| { type: 'RETRY' }
| { type: 'done.invoke.authenticate'; data: { id: string; name: string } }
| { type: 'error.platform.authenticate'; data: Error };
const authMachine = createMachine<AuthContext, AuthEvent>({
id: 'auth',
initial: 'idle',
context: {
user: null,
error: null,
retries: 0,
},
states: {
idle: {
on: {
LOGIN: 'authenticating',
},
},
authenticating: {
invoke: {
id: 'authenticate',
src: 'authenticateUser',
onDone: {
target: 'authenticated',
actions: assign({ user: (_, event) => event.data, error: null }),
},
onError: {
target: 'error',
actions: assign({
error: (_, event) => event.data.message,
retries: (ctx) => ctx.retries + 1,
}),
},
},
},
authenticated: {
on: {
LOGOUT: {
target: 'idle',
actions: assign({ user: null }),
},
},
},
error: {
on: {
RETRY: {
target: 'authenticating',
guard: 'canRetry',
},
LOGIN: 'authenticating',
},
},
},
});
export { authMachine };
Details
Context vs state: "State" in XState means the finite state node (idle, loading, error). "Context" is the extended state — arbitrary data that travels with the machine. Use finite states for mode/phase, context for data.
XState v5 changes: If using XState v5, the API shifts to setup().createMachine() with a different structure:
import { setup, assign } from 'xstate';
const machine = setup({
types: {} as {
context: AuthContext;
events: AuthEvent;
},
guards: { canRetry: ({ context }) => context.retries < 3 },
actions: { clearUser: assign({ user: null }) },
}).createMachine({
id: 'auth',
initial: 'idle',
context: { user: null, error: null, retries: 0 },
states: {
/* ... */
},
});
State node types:
atomic— leaf node, no child states (default)compound— has child states (nested)parallel— all child states active simultaneouslyfinal— terminal state, triggersonDonein the parenthistory— remembers the last active child state
Design principles:
- Every event should be handled in every state, even if the handler is a no-op. Unhandled events are silently ignored, which can mask bugs.
- Draw the state diagram first, code second. XState machines are visual — use Stately.ai or the VS Code extension.
- Prefer fewer top-level states with nested substates over many flat states.
Source
https://stately.ai/docs/machines
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.