Install
$ agentstack add skill-sablier-labs-agent-skills-xstate-react ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
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 (
send({ type: "TOGGLE" })}>
{state.value === "inactive" ? "Off" : "On"}
);
}
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 (
Current state: {JSON.stringify(state.value)}
Context: {JSON.stringify(state.context)}
send({ type: "SOME_EVENT" })}>Send
);
}
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 (
{state.context.todos.map((todo) => (
{todo.text}
))}
);
}
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 {count} todos;
}
function IsLoading({ actorRef }) {
// Only re-renders when loading state changes
const isLoading = useSelector(actorRef, (state) => state.matches("loading"));
return isLoading ? : 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 strategies
See ./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) |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: sablier-labs
- Source: sablier-labs/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.