RepoPilot

statelyai/xstate

State machines, statecharts, and actors for complex logic

Healthy

Healthy across all four use cases

HealthyDependency

Permissive license, no critical CVEs, actively maintained — safe to depend on.

HealthyFork & modify

Has a license, tests, and CI — clean foundation to fork and modify.

HealthyLearn from

Documented and popular — useful reference codebase to read through.

HealthyDeploy as-is

No critical CVEs, sane security posture — runnable as-is.

  • Concentrated ownership — top contributor handles 53% of recent commits
  • Last commit today
  • 11 active contributors
  • MIT licensed
  • CI configured
  • Tests present

Computed from maintenance signals — commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against dependency CVEs from deps.dev and OpenSSF Scorecard

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Want this for your own repo?

Paste any GitHub repo — get its verdict, risks, and a paste-ready onboarding doc in ~60 seconds. Free, no sign-up.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/statelyai/xstate)](https://repopilot.app/r/statelyai/xstate)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card

This card auto-renders when someone shares https://repopilot.app/r/statelyai/xstate on X, Slack, or LinkedIn.

Ask AI about statelyai/xstate

Grounded in the actual source code. Pick a starter question or write your own.

Or write your own question

Onboarding doc

Onboarding: statelyai/xstate

Generated by RepoPilot · 2026-07-12 · Source

Verdict

Healthy — Healthy across all four use cases

  • Last commit today
  • 11 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 53% of recent commits

Computed from maintenance signals — commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against dependency CVEs from deps.dev and OpenSSF Scorecard

TL;DR

XState is an event-driven state machine and actor orchestration library for JavaScript/TypeScript with zero dependencies. It implements SCXML-compliant statecharts to model complex application logic (UI flows, workflows, async coordination) as deterministic, visual state graphs that transition on discrete events. Core capability: define state machines in packages/core/src/createMachine.ts and spawn child actors that communicate via message passing through the Mailbox.ts system. Monorepo (Yarn workspaces) with packages/core containing the state machine engine (StateMachine.ts, StateNode.ts, createMachine.ts), action system (packages/core/src/actions/*), actor implementations (packages/core/src/actors/* for promises, callbacks, observables), and graph utilities for path analysis. packages/core/src/createActor.ts bootstraps actor instances; Mailbox.ts and system.ts handle message-passing concurrency.

LLM-derived; treat as a starting point, not verified fact.

Who it's for

Frontend and backend developers building complex applications that require predictable state management beyond simple Redux patterns—particularly teams using React, Vue, or vanilla JS who need to model multi-step workflows, form state machines, async orchestration, or cross-actor communication without prop drilling.

LLM-derived; treat as a starting point, not verified fact.

Maturity & risk

Highly mature and production-ready. XState is v5+ with extensive TypeScript support (2.1M+ TS lines), comprehensive test coverage via Vitest, active sponsorship via Open Collective, and a thriving Discord community. The monorepo structure with multiple packages (@xstate/store, xstate core) and official templates (React, Vanilla TS on CodeSandbox) indicates well-maintained active development.

Low risk for core adoption—zero external dependencies, MIT-licensed, and strong community backing. Main concerns: v5 introduced breaking changes from v4 (consult migration docs), and depth of SCXML specification knowledge required for advanced features like parallel states and hierarchical machines can steepen the learning curve. Single primary maintainer (David Khourshid) via the Stately org, though community is active.

LLM-derived; treat as a starting point, not verified fact.

Active areas of work

Active v5 development with focus on simplified API, improved TypeScript inference, and actor model maturity. Changelog-driven via Changesets (changeset script), with ongoing work on @xstate/store (lightweight event-based store alternative), vscode extension integration, and Stately Studio visual editing alignment. Recent efforts visible in packages/core/src/setup.ts (modern machine definition) and inspection APIs (packages/core/src/inspection.ts).

LLM-derived; treat as a starting point, not verified fact.

Get running

git clone https://github.com/statelyai/xstate.git
cd xstate
pnpm install  # enforced via preinstall script (scripts/ensure-pnpm.js)
pnpm test:core  # run core package tests (vitest)
pnpm build  # preconstruct build

Daily commands:

pnpm test:core:watch          # watch-mode tests for core package
pnpm test:watch               # all package tests in watch mode
pnpm build                     # compile via preconstruct

No traditional dev server—this is a library. Use templates: codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vanilla-ts or stackblitz link in README.

Map of the codebase

  • packages/core/src/createMachine.ts — Entry point for defining state machines; all state machine definitions flow through this factory function.
  • packages/core/src/StateMachine.ts — Core state machine implementation managing state transitions, event handling, and effect execution.
  • packages/core/src/createActor.ts — Instantiates executable actors from machine definitions; bridges machine definition to runtime behavior.
  • packages/core/src/StateNode.ts — Represents individual states within a machine tree; handles state hierarchy, transitions, and guards.
  • packages/core/src/types.ts — TypeScript type definitions for all public APIs; essential for understanding contract between modules.
  • packages/core/src/transition.ts — Computes next state from current state and event; core deterministic transition logic.
  • packages/core/src/actions.ts — Side-effect system including assign, send, spawn, and custom actions executed during state changes.

Components & responsibilities

  • StateMachine (TypeScript class with StateNode hierarchy) — Holds machine definition tree (StateNodes), guards, and initial state.
    • Failure mode: Malformed or circular state definitions cause invalid machine at creation time.
  • Actor (execution runtime) (Mailbox, event loop, action executors) — Executes state transitions, manages mailbox, spawns children, emits snapshots.
    • Failure mode: Unhandled errors in actions or guards cause actor to stop; error sent to parent.
  • Transition logic (Guard functions, event matching, StateNode traversal) — Evaluates guards, selects target states, computes enabled actions from event.
    • Failure mode: Guard exceptions propagate; invalid target states caught at machine creation.
  • Action executor (ActorScope injection, enqueueActions batch) — Executes side effects (assign, send, spawn, cancel) after state transition.
    • Failure mode: Action errors logged; can cause actor to stop if not handled.
  • Mailbox (Event queue with deferred processing) — FIFO event queue preventing stack overflow and ensuring predictable message order.
    • Failure mode: Overflow on recursive sends (mitigation: mailbox processes in batches).
  • Graph analyzer (DFS/BFS, adjacency list, path deduplication) — Traverses state space to generate paths, shortest routes, and test coverage models.
    • Failure mode: Unreachable states or infinite loops in graph traversal cause hangs.
  • Framework integration layer (useEffect/watch subscriptions, snapshot state sync) — Bridges actor lifecycle to React/Vue/Solid reactivity (hooks, composables).
    • Failure mode: Subscription leaks or stale snapshots if cleanup not called on unmount.

Data flow

  • User codecreateMachine() — Machine definition (states, events, guards, actions) flows in.
  • createMachine()StateMachine instance — Machine object with StateNode tree and metadata created.
  • StateMachinecreateActor() — Machine passed to factory for instantiation into executable actor.
  • Event (send)Mailbox → Transition — User event queued, then evaluated against current state for next state.
  • Transition resultAction executor — Enabled actions unpacked and executed in order (assign, send, spawn, etc.).
  • Action resultsSnapshot emission — State, context, spawned actors packaged as snapshot and sent to subscribers.

How to make changes

Add a custom action

  1. Define action type and parameters in your machine setup or inline. (packages/core/src/actions.ts)
  2. Create action function that receives ActorScope and executes side effects. (packages/core/src/actions/assign.ts (reference implementation))
  3. Use in machine definition via enqueueActions or direct action property. (packages/core/src/actions/enqueueActions.ts)

Add a new actor type

  1. Implement actor logic function matching ActorLogic<Snapshot, Event> interface. (packages/core/src/types.ts)
  2. Reference new actor in spawn() or spawnChild action with fromPromise/fromCallback pattern. (packages/core/src/spawn.ts)
  3. Optionally add framework-specific bindings in xstate-react, xstate-solid, etc. (packages/xstate-react/src/useActor.ts (reference))

Add a framework integration

  1. Create new package directory (e.g., packages/xstate-angular). (packages/xstate-react/src/useMachine.ts (reference implementation))
  2. Implement core hooks/composables using createActor and subscription patterns. (packages/xstate-solid/src/useActor.ts (reference for subscription model))
  3. Export public API matching framework conventions. (packages/xstate-react/src/index.ts (reference))

Add graph analysis or testing capability

  1. Implement path generator or visitor function operating on graph representation. (packages/core/src/graph/pathGenerators.ts (reference))
  2. Use graph utility functions to traverse edges and states. (packages/core/src/graph/utils.ts)
  3. Export from graph index for external use. (packages/core/src/graph/index.ts)

Why these technologies

  • TypeScript — Provides compile-time type safety for complex state machine definitions and actor logic.
  • Zero dependencies — Minimizes bundle size and distribution friction; pure JavaScript state management.
  • Actor model — Enables modular, concurrent state machine composition without shared mutable state.
  • SCXML-inspired syntax — Aligns with W3C statechart standard for broad interoperability and familiarity.
  • Graph analysis utilities — Enables automated testing, coverage analysis, and visualization of state space.

Trade-offs already made

  • Synchronous transition execution by default

    • Why: Deterministic, predictable state changes without async ambiguity.
    • Consequence: Async side effects must be wrapped in spawned actors, adding boilerplate.
  • Immutable snapshots, mutable context via assign()

    • Why: Balances update tracing with practical context mutation.
    • Consequence: Developers must be aware of assign() semantics; deep mutations can cause bugs.
  • Monorepo with separate framework packages

    • Why: Reduces core bundle and allows framework-agnostic usage.
    • Consequence: Integration requires learning framework-specific hooks for each platform.
  • Mailbox-based actor message queuing

    • Why: Prevents stack overflow in recursive event sends; FIFO processing order.
    • Consequence: Slightly higher memory overhead and latency vs. immediate dispatch.

Non-goals (don't propose these)

  • Real-time distributed consensus (local orchestration only)
  • Persistence layer (must be implemented by user via assign/send actions)
  • Built-in authentication or authorization
  • Time-series or analytics (separate concern)

Code metrics

  • Avg cyclomatic complexity: ~6 — Heavy use of recursive StateNode traversal, guard evaluation, and graph algorithms; moderate cyclomatic complexity from branching transition logic.
  • Largest file: packages/core/src/StateMachine.ts (450 lines)
  • Estimated quality issues: ~3 — Some error handling in actors is implicit; graph algorithms can loop indefinitely on malformed machines; framework integrations have subscription leak risk if cleanup not enforced.

Anti-patterns to avoid

  • Unhandled promise rejections in spawned promise actors (High)packages/core/src/actors/promise.ts: Spawned promise actors that reject without catch can silently fail; no error propagation to parent.
  • Recursive event sends causing mailbox growth (Medium)packages/core/src/Mailbox.ts: Machine sending events to itself in action logic can rapidly fill mailbox if not bounded.
  • Guard functions with side effects (Medium)packages/core/src/guards.ts: Guards are called multiple times per transition; embedding side effects causes unpredictable execution count.
  • Forgetting to clean up spawned actors (Medium)packages/core/src/actions/stopChild.ts: Long-lived machines can accumulate child actors if not explicitly stopped, causing memory leaks.

Performance hotspots

  • packages/core/src/transition.ts (CPU-bound state tree traversal) — Evaluates all possible transitions for an event; O(N) where N = enabled state nodes.
  • packages/core/src/graph/shortestPaths.ts (Combinatorial graph search) — BFS over full state graph for test coverage; exponential in state count for highly connected machines.
  • packages/core/src/Mailbox.ts (Single-threaded event loop) — Synchronous event processing loop drains entire queue in single tick; large bursts can block.

Traps & gotchas

pnpm required: scripts/ensure-pnpm.js enforces pnpm; npm/yarn will fail. preconstruct dev mode: postinstall runs preconstruct dev, which symlinks source—rebuilds required after edits in test scenarios. TypeScript strict mode: core uses strict flags; type inference is aggressive and may catch implicit any. Actor scope: actors do not auto-terminate child actors; explicit stopChild() action needed or they persist in system. Clock mocking: SimulatedClock.ts for testing; real timers are not mocked by default in actors created with createActor()—pass clock in options.

Architecture

Concepts to learn

  • Finite State Machine (FSM) — Core abstraction of XState—understanding states, transitions, and events is mandatory to use the library effectively
  • Statechart (Harel Statechart) — XState extends FSMs with hierarchical states, parallel regions, and deferred actions per the Harel model—required for modeling complex workflows
  • Actor Model — XState implements Erlang-like actor semantics (createActor(), message passing via Mailbox.ts, child spawning); essential for understanding concurrent orchestration
  • SCXML (State Chart XML) — W3C standard that XState aims to implement; defines syntax and semantics for executable statecharts, directly influences machine definition API
  • Guard Condition — XState guards in packages/core/src/guards.ts conditionally enable transitions based on context; critical for data-driven state logic
  • Deferred Effects (enqueueActions) — XState queues side effects (assign, send, spawn) rather than executing immediately; enables atomic transitions and simplified testing via enqueueActions.ts
  • Reachability Analysis & Graph Traversal — Tools in packages/core/src/graph/ compute all valid state paths and transitions for visualization, testing, and coverage validation
  • redux/redux — Alternative state management library; XState complements Redux for complex orchestration where Redux alone feels rigid
  • pmndrs/zustand — Lightweight alternative to Redux; @xstate/store competes/pairs with it, and both used together for hybrid simple-state + statechart patterns
  • statelyai/stately-editor — Visual editor for XState machines; generates TypeScript machine definitions from drag-and-drop statechart diagrams (complementary tooling)
  • microsoft/state-machine-cat — Statechart syntax and visualization compiler; alternative DSL but XState is more runtime-focused, not diagram-first
  • scxml/scxml — SCXML specification reference (W3C standard); XState's design is directly inspired by SCXML and aims to implement subset of spec

PR ideas

Click to expand

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for packages/core/src/graph/ utilities

The graph module contains critical path-finding and validation logic (graph.ts, shortestPaths.ts, simplePaths.ts, pathGenerators.ts, validateMachine.ts) that appears to lack dedicated test coverage based on the file structure. These utilities are essential for model-based testing and visualization features in XState. Adding thorough unit tests would catch regressions in graph algorithms and improve confidence in state machine analysis features.

  • [ ] Create packages/core/src/graph/tests/graph.test.ts covering adjacency matrix generation and graph traversal
  • [ ] Create packages/core/src/graph/tests/shortestPaths.test.ts with edge cases (cycles, disconnected states, parallel regions)
  • [ ] Create packages/core/src/graph/tests/simplePaths.test.ts validating all possible paths in complex state machines
  • [ ] Create packages/core/src/graph/tests/validateMachine.test.ts testing validation rules for machine definitions
  • [ ] Add integration tests in packages/core/src/graph/tests/pathGenerators.test.ts for path generation strategies

Add integration tests for actor spawning and child actor lifecycle (packages/core/src/actions/spawnChild.ts and stopChild.ts)

The actor system is a core feature of XState, but spawn/stop child actor actions lack dedicated integration tests that verify parent-child communication, cleanup on parent termination, and edge cases like spawning during state exit. This is critical for users relying on actor composition and would prevent subtle bugs in production actor hierarchies.

  • [ ] Create packages/core/src/actions/tests/spawnChild.integration.test.ts covering spawn with initial state, input, and system parameter
  • [ ] Add tests for bidirectional communication between parent and spawned child actors
  • [ ] Add tests verifying child actors are terminated when parent state exits or machine stops
  • [ ] Create packages/core/src/actions/tests/stopChild.integration.test.ts testing explicit child termination and error handling
  • [ ] Add edge case tests for spawning multiple children and concurrent spawn/stop operations

Add type-safe testing utilities to packages/core/src/graph/TestModel.ts and document with examples

TestModel.ts exists but lacks comprehensive documentation and example tests showing how new contributors should use it for model-based testing. Given that XState emphasizes testability and the graph module includes path generation, documenting and expanding TestModel with type-safe helpers would enable users to write better state machine tests and provide a reference implementation for the testing patterns XState promotes.

  • [ ] Expand packages/core/src/graph/TestModel.ts with generic type parameters and chainable API for building test models
  • [ ] Create packages/core/src/graph/tests/TestModel.examples.test.ts with 3-4 real-world test scenarios (form validation, async flows, error recovery)
  • [ ] Add JSDoc with @example tags to TestModel methods documenting common testing patterns
  • [ ] Create a TypeScript declaration test file to verify type inference and IDE autocomplete work correctly
  • [ ] Add export of TestModel to packages/core/src/index.ts if not already exposed, and document in schema

Good first issues

  • Add missing test coverage for packages/core/src/actions/emit.ts—it's exposed but lacks dedicated test file; check test patterns in assign.ts or send.ts.
  • Document the setup() API in packages/core/src/setup.ts with JSDoc examples matching the migration guide—newer TypeScript inference is underdocumented vs. v4.
  • Create a runnable example for the actor scope pattern in packages/core/src/graph/actorScope.ts in the examples directory—file exists but no demo shows how to introspect actor families.

Top contributors

Click to expand

Recent commits

Click to expand
  • 9297fa8 — Version Packages (#5590) (github-actions[bot])
  • e913eeb — fix(core): enter parallel default when targeting an unvisited history child (#5589) (spokodev)
  • ab5aa56 — Version Packages (#5586) (github-actions[bot])
  • 830db8b — fix(core): prevent 'systemId already exists' in pure transition functions (#5575) (JSap0914)
  • a551a2b — docs: add missing https protocol to Stately Studio link (#5585) (RubenFricke)
  • cc087bf — ci: pin Node to 24.16.0 (#5577) (davidkpiano)
  • 806ae91 — Version Packages (davidkpiano)
  • bf8d327 — Update changeset config (davidkpiano)
  • b53dbf8 — Merge branch 'main' of https://github.com/statelyai/xstate (davidkpiano)
  • 2d71caa — Update gitignore (davidkpiano)

Security observations

Click to expand

XState is a state management library with a solid security foundation given its zero-dependency core design. The primary security concerns are: (1) the expanded attack surface from multiple framework integrations requiring coordinated security maintenance, (2) potential risks in serialization/deserialization mechanisms used by xstate-inspect without visible validation, (3) event handling and actor lifecycle management requiring strict input validation, and (4) debugging/inspection interfaces that should be production-hardened. The codebase lacks visible evidence of hardcoded secrets or injection vulnerabilities in the provided file structure. Recommended actions include enabling continuous dependency scanning, implementing strict input validation across event/

  • Medium · Incomplete Dependency Version Constraints — packages/*/package.json and root package.json. The package.json shows truncated dependency versions (e.g., '@babel/core': '^7.23.3' cut off). Without viewing complete dependency specifications, it's difficult to verify that all transitive dependencies are pinned to secure versions. The monorepo structure with multiple packages increases the attack surface if any package has vulnerable dependencies. Fix: Run 'npm audit' and 'pnpm audit' regularly. Implement automated dependency scanning in CI/CD pipeline. Consider using lock files (pnpm-lock.yaml) and reviewing transitive dependencies for known CVEs.
  • Medium · Multiple Framework Integrations Increase Attack Surface — packages/xstate-react, packages/xstate-solid, packages/xstate-store-angular, packages/xstate-store-preact. The codebase includes integrations with React (xstate-react), Solid (xstate-solid), Angular (xstate-store-angular), and Preact (xstate-store-preact). Each framework integration is a potential vector for vulnerabilities if not properly isolated. The cross-package dependencies could propagate security issues. Fix: Implement strict dependency isolation between packages. Maintain separate security reviews for each framework-specific package. Consider reducing the number of maintained integrations or using strict SemVer versioning to prevent transitive vulnerabilities.
  • Low · State Serialization and Deserialization — packages/xstate-inspect/src/serialize.ts. The xstate-inspect package includes a 'serialize.ts' file which may handle serialization of state machines. If deserialization is performed without proper validation, this could lead to prototype pollution or arbitrary code execution, especially in server-side contexts. Fix: Implement strict input validation for deserialized data. Use safe deserialization patterns. Add type guards and schema validation. Consider using a library like 'zod' or 'io-ts' for runtime type validation of deserialized state objects.
  • Low · Inspection and Debugging Interface — packages/xstate-inspect/src/browser.ts, packages/xstate-inspect/src/server.ts, packages/xstate-inspect/src/inspectMachine.ts. The xstate-inspect package provides debugging and inspection capabilities accessible via browser and server interfaces. If not properly secured, this could expose sensitive state information or allow unauthorized state modifications in production environments. Fix: Ensure inspection features are disabled in production builds. Implement authentication/authorization for inspection endpoints. Document security best practices for using xstate-inspect in development vs. production. Add environment-based feature flags to disable inspection in production.
  • Low · Actor Spawning and Process Management — packages/core/src/actions/spawnChild.ts, packages/core/src/actions/stopChild.ts, packages/core/src/actors/. The spawnChild and stopChild actions in the actors module handle process/actor lifecycle. If actor references or IDs can be manipulated, this could lead to unauthorized process termination or creation. Fix: Implement strict validation of actor IDs and references. Use unforgeable tokens or cryptographic identifiers for actor references. Document the security model for actor spawning and ensure caller identity is validated before allowing process lifecycle operations.
  • Low · Event Queue and Mailbox Processing — packages/core/src/Mailbox.ts, packages/core/src/eventUtils.ts. The Mailbox.ts and event processing logic handles external event inputs. If event handlers are not properly isolated or if event payloads are not validated, malicious event data could be injected. Fix: Implement strict event schema validation. Use TypeScript types to enforce event structure. Sanitize event payloads before processing. Consider implementing a size limit on event queues to prevent DoS attacks via event flooding.

LLM-derived; treat as a starting point, not a security audit.

The exported doc (Copy CLAUDE.md / Download / .cursor/rules) also includes an agent protocol and a verification script written for AI coding agents — omitted here to keep this view scannable.

Embed this chat in your README

Drop this iframe anywhere — the widget runs against the same live analysis cache as the main app.

<iframe
  src="https://repopilot.app/embed/statelyai/xstate"
  width="100%" height="500"
  style="border:1px solid #d0d7de; border-radius:8px;"
  allow="microphone"
  loading="lazy"
></iframe>