skills/xstate-react/SKILL.md
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.
npx skillsauth add sablier-labs/agent-skills xstate-reactInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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:
When to use simpler state instead (useState, Zustand):
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>
);
}
useMachineCreate 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>
);
}
useActorSubscribe 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>
);
}
useSelectorOptimize 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;
}
typesDefine 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: {
// ...
}
}
});
setup() for Reusable DefinitionsDefine 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"
}
}
}
}
});
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 holds extended state data:
const machine = createMachine({
context: {
count: 0,
user: null
},
// ...
});
// Access in component
const count = state.context.count;
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")
}
}
});
setup() for type-safe action and guard definitionsuseSelector for optimized state selectionstate.matches() for hierarchical state checkingassign)xState hooks must be used in Client Components. Add the "use client" directive:
"use client";
import { useMachine } from "@xstate/react";
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 strategies
See ./references/patterns.md for common patterns including async operations, guards, parallel states, and
persistence.
| 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) |
tools
This skill should be used when the user asks about "viem", "viem client", "viem actions", "TypeScript Ethereum", "createPublicClient", "createWalletClient", "parseEther", "formatEther", "readContract", "writeContract", or mentions using viem for blockchain interactions.
development
This skill should be used when the user asks about "Tailwind CSS", "tailwind-variants", "tv() function", "CSS-first configuration", "Tailwind breaking changes", mentions styling with Tailwind utilities, gradient syntax, or component variants with TypeScript.
development
This skill should be used when the user asks to "analyze a screenshot", "generate implementation spec", "create SPEC.md from screenshot", "extract design specs", "spec from image", or provides website screenshots and wants detailed implementation guidance.
documentation
This skill should be used when writing content for Sablier, including "write a blog post", "create a case study", "draft a tweet", "write X/Twitter posts", "write an announcement", "create educational content", or any marketing content task for Sablier's brand.