xstate-react
Skill9 starsChanged 51 days agoMITView on GitHub
This skill should be used when the user asks to "create a state machine", "add xState", "use xState with React", "implement actor-based state", "manage complex state with state machines", "use xState with Effect", "integrate Effect-ts with xState", mentions xState hooks (useMachine, useActor, useSelector), or discusses finite state machines in React applications.
In this repository9 other files it gives its agents.
- bttSkill
- cli-forgeSkill
- evm-deploymentSkill
- sablier-designSkill
- sablier-iconSkill
- sablier-writingSkill
- spec-from-screenshotSkill
- tailwind-cssSkill
- viemSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 6 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Create a state machine and use it in a React component:
- Distinctive: line 72
Create and run a machine within a component's lifecycle:
- Very specific: line 342
Use setup() for type-safe action and guard definitions
- Very specific: line 343
Use useSelector for optimized state selection
- Very specific: line 345
Use state.matches() for hierarchical state checking
- Distinctive: line 346
Keep machines pure and side-effect-free (use actions for effects)
- Distinctive: line 350
Don't store the entire snapshot object in React state
- Very specific: line 351
Don't mutate context directly (use assign)
- Distinctive: line 352
Don't use xState for simple toggle/counter state
- Distinctive: line 353
Don't forget to handle all possible states in your UI
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
xState React
Your Role
You are an expert in xState v5 actor-based state management with React and TypeScript. You understand state machines, statecharts, the actor model, and React integration patterns for managing complex application logic.
Overview
xState is an actor-based state management and orchestration solution for JavaScript and TypeScript applications. It uses event-driven programming, state machines, statecharts, and the actor model to handle complex logic in predictable, robust, and visual ways.
When to use xState:
- Complex UI flows (multi-step forms, wizards, checkout processes)
- State with many transitions and edge cases
- Logic that needs to be visualized and validated
- Processes with async operations and error handling
- State that can be in multiple "modes" (loading, error, success, idle)
When to use simpler state instead (useState, Zustand):
- Simple UI toggles and counters
- Form state confined to a single component
- State without complex transition logic
- CRUD operations with straightforward loading states
Quick Start
Create a state machine and use it in a React component:
"use client";
import { createMachine } from "xstate";
import { useMachine } from "@xstate/react";
const toggleMachine = createMachine({
id: "toggle",
initial: "inactive",
states: {
inactive: {
on: { TOGGLE: "active" }
},
active: {
on: { TOGGLE: "inactive" }
}
}
});
function Toggle() {
const [state, send] = useMachine(toggleMachine);
return (
<button onClick={() => send({ type: "TOGGLE" })}>
{state.value === "inactive" ? "Off" : "On"}
</button>
);
}React Hooks API
useMachine
Create and run a machine within a component's lifecycle:
import { useMachine } from "@xstate/react";
import { someMachine } from "./machines/someMachine";
function Component() {
const [state, send, actorRef] = useMachine(someMachine, {
input: { userId: "123" } // Optional input
});
return (
<div>
<p>Current state: {JSON.stringify(state.value)}</p>
<p>Context: {JSON.stringify(state.context)}</p>
<button onClick={() => send({ type: "SOME_EVENT" })}>Send</button>
</div>
);
}useActor
Subscribe to an existing actor (created outside the component):
import { createActor } from "xstate";
import { useActor } from "@xstate/react";
import { todoMachine } from "./machines/todoMachine";
// Create actor outside component (e.g., in a module or context)
const todoActor = createActor(todoMachine);
todoActor.start();
function TodoApp() {
const [state, send] = useActor(todoActor);
return (
<ul>
{state.context.todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}useSelector
Optimize re-renders by selecting specific state:
import { useSelector } from "@xstate/react";
function TodoCount({ actorRef }) {
// Only re-renders when todos.length changes
const count = useSelector(actorRef, (state) => state.context.todos.length);
return <span>{count} todos</span>;
}
function IsLoading({ actorRef }) {
// Only re-renders when loading state changes
const isLoading = useSelector(actorRef, (state) => state.matches("loading"));
return isLoading ? <Spinner /> : null;
}TypeScript Patterns
Typing Machines with types
Define context and events using the types property:
import { createMachine, assign } from "xstate";
type FormContext = {
name: string;
email: string;
errors: string[];
};
type FormEvent =
| { type: "UPDATE_NAME"; value: string }
| { type: "UPDATE_EMAIL"; value: string }
| { type: "SUBMIT" }
| { type: "RESET" };
const formMachine = createMachine({
types: {} as {
context: FormContext;
events: FormEvent;
},
id: "form",
initial: "editing",
context: {
name: "",
email: "",
errors: []
},
states: {
editing: {
on: {
UPDATE_NAME: {
actions: assign({
name: ({ event }) => event.value
})
},
UPDATE_EMAIL: {
actions: assign({
email: ({ event }) => event.value
})
},
SUBMIT: "submitting"
}
},
submitting: {
// ...
}
}
});Using setup() for Reusable Definitions
Define actions, guards, actors, and delays in a type-safe way:
import { setup, assign } from "xstate";
type AuthContext = {
userId: string | null;
retries: number;
};
type AuthEvent =
| { type: "LOGIN"; username: string; password: string }
| { type: "LOGOUT" }
| { type: "SUCCESS"; userId: string }
| { type: "FAILURE" };
const authMachine = setup({
types: {} as {
context: AuthContext;
events: AuthEvent;
},
actions: {
setUser: assign({
userId: ({ event }) => (event as { userId: string }).userId
}),
clearUser: assign({
userId: null,
retries: 0
}),
incrementRetries: assign({
retries: ({ context }) => context.retries + 1
})
},
guards: {
hasReachedMaxRetries: ({ context }) => context.retries >= 3,
isAuthenticated: ({ context }) => context.userId !== null
}
}).createMachine({
id: "auth",
initial: "loggedOut",
context: { userId: null, retries: 0 },
states: {
loggedOut: {
on: {
LOGIN: "authenticating"
}
},
authenticating: {
on: {
SUCCESS: {
target: "loggedIn",
actions: "setUser"
},
FAILURE: [
{
guard: "hasReachedMaxRetries",
target: "loggedOut",
actions: "clearUser"
},
{
actions: "incrementRetries"
}
]
}
},
loggedIn: {
on: {
LOGOUT: {
target: "loggedOut",
actions: "clearUser"
}
}
}
}
});Core Concepts
States and Transitions
States represent the possible modes of your system. Transitions define how events move between states:
const machine = createMachine({
initial: "idle",
states: {
idle: {
on: { FETCH: "loading" }
},
loading: {
on: {
SUCCESS: "success",
ERROR: "error"
}
},
success: { type: "final" },
error: {
on: { RETRY: "loading" }
}
}
});Context
Context holds extended state data:
const machine = createMachine({
context: {
count: 0,
user: null
},
// ...
});
// Access in component
const count = state.context.count;Actions
Actions are fire-and-forget side effects:
import { assign } from "xstate";
const machine = createMachine({
// ...
states: {
active: {
entry: assign({ count: ({ context }) => context.count + 1 }),
exit: () => console.log("Leaving active state")
}
}
});Best Practices
DO
- Use
setup()for type-safe action and guard definitions - Use
useSelectorfor optimized state selection - Define explicit types for context and events
- Use
state.matches()for hierarchical state checking - Keep machines pure and side-effect-free (use actions for effects)
DON'T
- Don't store the entire snapshot object in React state
- Don't mutate context directly (use
assign) - Don't use xState for simple toggle/counter state
- Don't forget to handle all possible states in your UI
Client Component Requirement
xState hooks must be used in Client Components. Add the "use client" directive:
"use client";
import { useMachine } from "@xstate/react";Additional Documentation
For comprehensive xState documentation including advanced patterns, use the Context7 MCP:
Use Context7 MCP with library ID "/statelyai/xstate" to fetch:
- Detailed invoke/spawn patterns
- Parallel and history states
- Actor communication
- Testing strategiesSee ./references/patterns.md for common patterns including async operations, guards, parallel states, and
persistence.
Quick Reference
| Task | Pattern |
| Create machine | createMachine({ id, initial, states, context }) |
| Use in component | const [state, send] = useMachine(machine) |
| Check state | state.matches("loading") or state.value |
| Send event | send({ type: "EVENT_NAME", ...payload }) |
| Read context | state.context.someValue |
| Update context | assign({ key: ({ context, event }) => newValue }) |
| Conditional | guard: "guardName" or guard: ({ context }) => … |
| Async operation | invoke: { src: fromPromise(...), onDone, onError } |
| Optimized select | useSelector(actorRef, (state) => state.context.x) |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

