AgentStack
SKILL verified MIT Self-run

Xstate React

skill-sablier-labs-agent-skills-xstate-react · by sablier-labs

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.

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-sablier-labs-agent-skills-xstate-react

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

Are you the author of Xstate React? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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 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 & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.