smithery.ai

xstate-react

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.

First seen Apr 15, 2026

Installation

$ npx skills add https://smithery.ai

Summary

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.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,531 B
  • docs SUMMARY.md 385 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

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.

See ./references/EFFECTTSINTEGRATION.md for integrating Effect-ts with xState for composable, type-safe side effects.

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)