Skip to content
Version: XState v4

Running machines

We’ve now covered configuring statecharts and setting up their states, transitions, and options.

Next, we need to learn how to execute our machines in code. Depending on where you’re using XState, you’ll likely need to run machines differently. We’ll start with the VanillaJS approach for now.

Interpret

The interpret interpreter provides us with a “running” and interactive version of our machine.

import { createMachine, interpret } from 'xstate';

const machine = createMachine({});

const actor = interpret(machine).start();

In XState, we call that “running” machine an actor. The most common way to interact with an actor is to send it events:

import { createMachine, interpret } from 'xstate';

const machine = createMachine(
{
on: {
SAY_HELLO: {
actions: 'sayHello',
},
},
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
}
);

const actor = interpret(machine).start();

actor.send({
type: 'SAY_HELLO',
});

// Logs 'Hello!'

You can also subscribe to the actor via actor.subscribe:

import { createMachine, interpret } from 'xstate';

const machine = createMachine(
{
on: {
SAY_HELLO: {
actions: 'sayHello',
},
},
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
}
);

const actor = interpret(machine).start();

// Fires whenever the state changes
const { unsubscribe } = actor.subscribe((state) => {
console.log(
// We’ll learn what goes inside here soon!
state
);
});

You can also stop the actor by running actor.stop() which cleans up the actor and runs any relevant exit actions:

import { createMachine, interpret } from 'xstate';

const machine = createMachine(
{
on: {
SAY_HELLO: {
actions: 'sayHello',
},
},
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
}
);

const actor = interpret(machine).start();

actor.stop();

interpret can run anywhere JavaScript runs, which means you can run XState in the browser, Node, Electron, React Native… anywhere!

Check out the reference docs on the interpret API for a full deep dive into everything a actor can do.

State API

When running your machine, you’ll want to query the machine to understand which state it’s in. When you run a machine using interpret, you can find the state as follows:

import { createMachine, interpret } from 'xstate';

const machine = createMachine({});

const actor = interpret(machine).start();

const state = actor.state;

You can also get an updated version when you subscribe to the state:

import { createMachine, interpret } from 'xstate';

const machine = createMachine({});

const actor = interpret(machine).start();

actor.subscribe((state) => {
console.log(state);
});

This State class has a bunch of useful attributes and methods.

Context

First, you can find the state’s context using state.context.

import { createMachine, interpret, assign } from 'xstate';

const machine = createMachine(
{
context: {
count: 0,
},
on: {
INCREMENT: {
actions: 'incrementCount',
},
},
},
{
actions: {
incrementCount: assign((context) => {
return {
count: context.count + 1,
};
}),
},
}
);

const actor = interpret(machine).start();

actor.subscribe((state) => {
console.log(state.context.count);
});

actor.send({ type: 'INCREMENT' });

state.matches

You can query which state the machine is in by running state.matches(). state.matches() returns a boolean depending on whether you’re in a matching state.

import { createMachine, interpret, assign } from 'xstate';

const machine = createMachine({
initial: 'toggledOff',
states: {
toggledOff: {
on: {
TOGGLE: 'toggledOn',
},
},
toggledOn: {
on: {
TOGGLE: 'toggledOff',
},
},
},
});

const actor = interpret(machine).start();

actor.subscribe((state) => {
const isToggledOn = state.matches('toggledOn');

console.log(isToggledOn); // true / false
console.log(state.value); // 'toggledOn' / 'toggledOff'
});

You can also use state.value to check your machine’s state, but we recommend using state.matches because of how it works with parent and child states. We’ll talk more about parent states later. Jump directly to parent and child states.

Changed

You can check if a state was changed by the most recently received event. The state was changed if:

  1. There was a change in state.value. For example, moving from toggledOn to toggledOff.
  2. A value in context was changed.

Running machines as pure functions

You can use machine.transition to run your machine as a pure function without executing any of its actions. machine.transition is a pure function that takes two arguments:

  • state, the State to transition from
  • event, the event that causes the transition

machine.transition returns a new State resulting from taking all the transitions enabled by the current state and event.

const machine = createMachine({
/* ... */
});

const initialState = machine.initialState;

// determine next state based on current state and event
const yellowState = machine.transition(initialState, {
type: 'TIMER',
});

console.log(yellowState.value);
// => 'yellow'