- Get started
- Cheatsheet
Version: XState v5
Cheatsheet
Use this cheatsheet to quickly look up the syntax for XState v5. stateMachine1
Installing XState
- npm
- pnpm
- yarn
npm install xstate
Read more on installing XState.
Creating a state machine
import { setup, createActor, assign } from 'xstate';
const machine = setup({/* ... */})
.createMachine({
id: 'toggle',
initial: 'active',
context: { count: 0 },
states: {
active: {
entry: assign({
count: ({ context }) => context.count + 1,
}),
on: {
toggle: { target: 'inactive' },
},
},
inactive: {
on: {
toggle: { target: 'active' },
},
},
},
});
const actor = createActor(machine);
actor.subscribe((snapshot) => {
console.log(snapshot.value);
});
actor.start();
// logs 'active' with context { count: 1 }
actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 1 }
actor.send({ type: 'toggle' });
// logs 'active' with context { count: 2 }
actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 2 }
Read more about the actor model.
Creating promise logic
import { fromPromise, createActor } from 'xstate';
const promiseLogic = fromPromise(async () => {
const response = await fetch('https://dog.ceo/api/breeds/image/random');
const dog = await response.json();
return dog;
});
const actor = createActor(promiseLogic);
actor.subscribe((snapshot) => {
console.log(snapshot);
});
actor.start();
// logs: {
// message: "https://images.dog.ceo/breeds/kuvasz/n02104029_110.jpg",
// status: "success"
// }
Read more about promise actor logic.
Creating transition logic
A transition function is just like a reducer.
import { fromTransition, createActor } from 'xstate';
const transitionLogic = fromTransition(
(state, event) => {
switch (event.type) {
case 'inc':
return {
...state,
count: state.count + 1,
};
default:
return state;
}
},
{ count: 0 }, // initial state
);
const actor = createActor(transitionLogic);
actor.subscribe((snapshot) => {
console.log(snapshot);
});
actor.start();
// logs { count: 0 }
actor.send({ type: 'inc' });
// logs { count: 1 }
actor.send({ type: 'inc' });
// logs { count: 2 }
Read more about transition actors.
Creating observable logic
import { fromTransition, createActor } from 'xstate';
const transitionLogic = fromTransition(
(state, event) => {
switch (event.type) {
case 'inc':
return {
...state,
count: state.count + 1,
};
default:
return state;
}
},
{ count: 0 }, // initial state
);
const actor = createActor(transitionLogic);
actor.subscribe((snapshot) => {
console.log(snapshot);
});
actor.start();
// logs { count: 0 }
actor.send({ type: 'inc' });
// logs { count: 1 }
actor.send({ type: 'inc' });
// logs { count: 2 }
Read more about observable actors.
Creating callback logic
import { setup, createActor } from 'xstate';
const machine = setup({/* ... */})
.createMachine({
id: 'parent',
initial: 'active',
states: {
active: {
initial: 'one',
states: {
one: {
on: {
NEXT: { target: 'two' }
}
},
two: {},
},
on: {
NEXT: { target: 'inactive' }
}
},
inactive: {},
},
});
const actor = createActor(machine);
actor.subscribe((snapshot) => {
console.log(snapshot.value);
});
actor.start();
// logs { active: 'one' }
actor.send({ type: 'NEXT' });
// logs { active: 'two' }
actor.send({ type: 'NEXT' });
// logs 'inactive'
Read more about callback actors.
Parent states
import { setup, createActor } from 'xstate';
const machine = setup({
actions: {
activate: () => {/* ... */},
deactivate: () => {/* ... */},
notify: (_, params: { message: string }) => {/* ... */},
}
}).createMachine({
id: 'toggle',
initial: 'active',
states: {
active: {
entry: { type: 'activate' },
exit: { type: 'deactivate' },
on: {
toggle: {
target: 'inactive',
actions: [{ type: 'notify' }],
},
},
},
inactive: {
on: {
toggle: {
target: 'active',
actions: [
// action with params
{
type: 'notify',
params: {
message: 'Some notification',
},
},
],
},
},
},
},
});
const actor = createActor(
machine.provide({
actions: {
notify: (_, params) => {
console.log(params.message ?? 'Default message');
},
activate: () => {
console.log('Activating');
},
deactivate: () => {
console.log('Deactivating');
},
},
}),
);
actor.start();
// logs 'Activating'
actor.send({ type: 'toggle' });
// logs 'Deactivating'
// logs 'Default message'
actor.send({ type: 'toggle' });
// logs 'Some notification'
// logs 'Activating'
Read more about parent states.
Actions
import { setup, createActor } from 'xstate';const machine = setup({ actions: { activate: () => {/* ... */}, deactivate: () => {/* ... */}, notify: (_, params: { message: string }) => {/* ... */}, }}).createMachine({ id: 'toggle', initial: 'active', states: { active: { entry: { type: 'activate' }, exit: { type: 'deactivate' }, on: { toggle: { target: 'inactive', actions: [{ type: 'notify' }], }, }, }, inactive: { on: { toggle: { target: 'active', actions: [ // action with params { type: 'notify', params: { message: 'Some notification', }, }, ], }, }, }, },});const actor = createActor( machine.provide({ actions: { notify: (_, params) => { console.log(params.message ?? 'Default message'); }, activate: () => { console.log('Activating'); }, deactivate: () => { console.log('Deactivating'); }, }, }),);actor.start();// logs 'Activating'actor.send({ type: 'toggle' });// logs 'Deactivating'// logs 'Default message'actor.send({ type: 'toggle' });// logs 'Some notification'// logs 'Activating'
Guards
import { setup, createActor } from 'xstate';const machine = setup({ guards: { canBeToggled: ({ context }) => context.canActivate, isAfterTime: (_, params) => { const { time } = params; const [hour, minute] = time.split(':'); const now = new Date(); return now.getHours() > hour && now.getMinutes() > minute; }, }, actions: { notifyNotAllowed: () => { console.log('Cannot be toggled'); }, },}).createMachine({ id: 'toggle', initial: 'active', context: { canActivate: false, }, states: { inactive: { on: { toggle: [ { target: 'active', guard: 'canBeToggled', }, { actions: 'notifyNotAllowed', }, ], }, }, active: { on: { toggle: { // Guard with params guard: { type: 'isAfterTime', params: { time: '16:00' } }, target: 'inactive', }, }, // ... }, },});const actor = createActor(machine);actor.start();// logs 'Cannot be toggled'
Invoking actors
import { setup, fromPromise, createActor, assign } from 'xstate';const loadUserLogic = fromPromise(async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users/1'); const user = await response.json(); return user;});const machine = setup({ actors: { loadUserLogic }}).createMachine({ id: 'toggle', initial: 'loading', context: { user: undefined, }, states: { loading: { invoke: { id: 'loadUser', src: 'loadUserLogic', onDone: { target: 'doSomethingWithUser', actions: assign({ user: ({ event }) => event.output, }), }, onError: { target: 'failure', actions: ({ event }) => { console.log(event.error) } } }, }, doSomethingWithUser: { // ... }, failure: { // ... }, },});const actor = createActor(machine);actor.subscribe((snapshot) => { console.log(snapshot.context.user);});actor.start();// eventually logs:// { id: 1, name: 'Leanne Graham', ... }
Read more about invoking actors.
Spawning actors
import { setup, fromPromise, createActor, assign } from 'xstate';const loadUserLogic = fromPromise(async () => { const response = await fetch('https://jsonplaceholder.typicode.com/users/1'); const user = await response.json(); return user;});const machine = setup({ actors: { loadUserLogic }}).createMachine({ context: { userRef: undefined, }, on: { loadUser: { actions: assign({ userRef: ({ spawn }) => spawn('loadUserLogic'), }), }, },});const actor = createActor(machine);actor.subscribe((snapshot) => { const { userRef } = snapshot.context; console.log(userRef?.getSnapshot());});actor.start();actor.send({ type: 'loadUser' });// eventually logs:// { id: 1, name: 'Leanne Graham', ... }
Read more about spawning actors.
Input and output
import { setup, createActor } from 'xstate';const greetMachine = setup({ types: { context: {} as { message: string }, input: {} as { name: string }, }}).createMachine({ context: ({ input }) => ({ message: `Hello, ${input.name}`, }), entry: ({ context }) => { console.log(context.message); },});const actor = createActor(greetMachine, { input: { name: 'David', },});actor.start();// logs 'Hello, David'
Invoking actors with input
import { setup, createActor, fromPromise } from 'xstate';const loadUserLogic = fromPromise(async ({ input }) => { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${input.id}`, ); const user = await response.json(); return user;});const machine = setup({ actors: { loadUserLogic }}).createMachine({ initial: 'loading user', states: { 'loading user': { invoke: { id: 'loadUser', src: 'loadUserLogic', input: { id: 3, }, onDone: { actions: ({ event }) => { console.log(event.output); }, }, }, }, },});const actor = createActor(machine);actor.start();// eventually logs:// { id: 3, name: 'Clementine Bauch', ... }
Read more about invoking actors with input.
Types
import { setup, fromPromise } from 'xstate';const promiseLogic = fromPromise(async () => { /* ... */});const machine = setup({ types: { context: {} as { count: number; }; events: {} as | { type: 'inc'; } | { type: 'dec' } | { type: 'incBy'; amount: number }; actions: {} as | { type: 'notify'; params: { message: string } } | { type: 'handleChange' }; guards: {} as | { type: 'canBeToggled' } | { type: 'isAfterTime'; params: { time: string } }; children: {} as { promise1: 'someSrc'; promise2: 'someSrc'; }; delays: 'shortTimeout' | 'longTimeout'; tags: 'tag1' | 'tag2'; input: number; output: string; }, actors: { promiseLogic }}).createMachine({ // ...});
Previous
Templates