xstate-react

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "xstate-react" with this command: npx skills add paulrberg/dot-agents/paulrberg-dot-agents-xstate-react

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 useSelector for 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 Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-simplify

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cli-gh

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-polish

No summary provided by upstream source.

Repository SourceNeeds Review