Getting Started
Welcome to XMachines! This guide will help you understand what XMachines is and get you up and running with your first state machine.
What is XMachines?
XMachines is a TypeScript implementation of the Universal Player Architecture—a state machine library that strictly separates business logic from infrastructure. It enables logic-driven applications where state machines control routing, views, and navigation through standardized TC39 Signals.
At its core, XMachines is built on the actor model, following the principle that business logic must be the single source of truth for navigation, state, and UI structure. Infrastructure reflects actor state—it never decides.
Unlike traditional frameworks where your logic adapts to the framework, XMachines inverts this relationship. Your state machines own the application behavior, and the infrastructure (routers, view renderers, React components) passively observes and reflects the current state through signals. This results in complete runtime agnosticism—your logic has zero framework dependencies.
Why XMachines?
Type Safety with TypeScript
- Full type inference from state machine catalog through to components
- Catch navigation errors and state bugs at compile time
- Zod validation for UI schemas and state structures
Predictable State Management
- State machines make application behavior explicit and testable
- Actor model ensures clear message passing patterns
- Guard enforcement prevents invalid state transitions
Platform Flexibility
- Runtime-agnostic core packages work in browser, Node, or Deno
- Choose only the packages you need for your platform
- Framework integrations (React, TanStack Router) are optional adapters
Modular Architecture
- 8 focused packages that work together seamlessly
@xmachines/playfoundation works with any infrastructure- Mix and match adapters based on your stack
Quick Start
Installation
First, install XMachines packages. See the complete Installation → guide for all package managers and environments.
npm install @xmachines/play @xmachines/play-xstateImport and Create Your First Machine
import { createMachine } from "@xmachines/play-xstate";
// Define a simple traffic light machineconst trafficLightMachine = createMachine({ id: "trafficLight", initial: "red", states: { red: { on: { TIMER: "green" }, }, green: { on: { TIMER: "yellow" }, }, yellow: { on: { TIMER: "red" }, }, },});Create an Actor and Transition States
import { createActor } from "@xmachines/play-actor";
// Instantiate the machine as an actorconst actor = createActor(trafficLightMachine);
// Start the actoractor.start();
// Check initial stateconsole.log(actor.getSnapshot().value); // "red"
// Send events to transition statesactor.send({ type: "TIMER" });console.log(actor.getSnapshot().value); // "green"
actor.send({ type: "TIMER" });console.log(actor.getSnapshot().value); // "yellow"Observe State Changes with Signals
import { signal, computed } from "@xmachines/play-signals";
// Create a signal to track actor stateconst currentState = signal(actor.getSnapshot().value);
// Subscribe to actor changesactor.subscribe((snapshot) => { currentState.value = snapshot.value;});
// Create computed valuesconst canProceed = computed(() => currentState.value === "green");
console.log(canProceed.value); // true when light is greenYour First Machine: Toggle Example
Here’s a complete working example that demonstrates the core concepts:
import { createMachine } from "@xmachines/play-xstate";import { createActor } from "@xmachines/play-actor";import { signal } from "@xmachines/play-signals";
// Define the machine configurationconst toggleMachine = createMachine( { id: "toggle", initial: "inactive", context: { count: 0, }, states: { inactive: { on: { TOGGLE: { target: "active", actions: "incrementCount", }, }, }, active: { on: { TOGGLE: { target: "inactive", actions: "incrementCount", }, }, }, }, }, { actions: { incrementCount: ({ context }) => ({ count: context.count + 1, }), }, },);
// Create and start the actorconst toggleActor = createActor(toggleMachine);toggleActor.start();
// Bind to a signal for reactive updatesconst state = signal(toggleActor.getSnapshot());
toggleActor.subscribe((snapshot) => { state.value = snapshot;});
// Use the machinetoggleActor.send({ type: "TOGGLE" });console.log(state.value.value); // "active"console.log(state.value.context.count); // 1
toggleActor.send({ type: "TOGGLE" });console.log(state.value.value); // "inactive"console.log(state.value.context.count); // 2Understanding the Parts
States: Discrete modes your application can be in (inactive, active)
Events: Messages that trigger transitions (TOGGLE)
Transitions: Rules that define which events move between which states (on: { TOGGLE: 'active' })
Context: Extended state data that persists across transitions (count: 0)
Actions: Side effects or context updates executed during transitions (incrementCount)
Actor: Runtime instance of a machine that processes events and emits state changes
Signals: Reactive primitives (TC39 Signals) that enable infrastructure to observe state changes without coupling
Next Steps
Now that you understand the basics, explore these resources:
Core Documentation
- API Reference - Detailed API documentation
Platform Guides
- React Integration - Use XMachines with React
- Router Integration - Logic-driven routing with TanStack Router
Examples
- Examples Directory - More code examples and usage patterns
- Working dashboard demo (coming soon)
Ready to install? → Installation Guide