What is xstate?
The xstate npm package is a library for creating, interpreting, and executing finite state machines and statecharts, as well as managing invocations of those machines as actors. It provides a robust framework for modeling and analyzing application logic in a composable and declarative way, which can help manage complex state logic in UIs, robotics, and other systems.
What are xstate's main functionalities?
Finite State Machines
This feature allows you to create simple finite state machines. The code sample demonstrates a toggle machine with two states: 'active' and 'inactive'.
{"import { Machine } from 'xstate';\n\nconst toggleMachine = Machine({\n id: 'toggle',\n initial: 'inactive',\n states: {\n inactive: { on: { TOGGLE: 'active' } },\n active: { on: { TOGGLE: 'inactive' } }\n }\n});"}
Statecharts
This feature allows you to create statecharts, which are an extension of state machines that can have hierarchical and parallel states. The code sample shows a traffic light machine with three states: 'green', 'yellow', and 'red'.
{"import { Machine } from 'xstate';\n\nconst lightMachine = Machine({\n id: 'light',\n initial: 'green',\n states: {\n green: { on: { TIMER: 'yellow' } },\n yellow: { on: { TIMER: 'red' } },\n red: { on: { TIMER: 'green' } }\n }\n});"}
Interpreters
This feature allows you to interpret and execute the state machines and statecharts. The code sample demonstrates how to start a service that interprets the toggleMachine and logs state transitions.
{"import { interpret } from 'xstate';\n\nconst service = interpret(toggleMachine).onTransition((state) => console.log(state.value));\n\nservice.start();\nservice.send('TOGGLE');\nservice.send('TOGGLE');"}
Actors
This feature allows you to manage machine invocations as actors, which can send and receive events from other machines. The code sample shows how to spawn a child machine within a parent machine's context.
{"import { spawn, Machine } from 'xstate';\n\nconst parentMachine = Machine({\n context: {\n child: null\n },\n entry: ['spawnChild']\n}, {\n actions: {\n spawnChild: assign({\n child: () => spawn(someMachine)\n })\n }\n});"}
Other packages similar to xstate
robot3
robot3 is a functional, immutable finite state machine library with a similar API to xstate. It is smaller in size but does not offer the full range of features such as hierarchical and parallel states.
state-machine-cat
state-machine-cat allows you to visualize state machines and statecharts. While it does not execute state machines like xstate, it is useful for documentation and analysis purposes.
redux-saga
redux-saga is a library that handles side effects in Redux applications using sagas, which are similar to state machines. It is more focused on managing side effects than modeling state, unlike xstate which is more general-purpose.
JavaScript state machines and statecharts
JavaScript and TypeScript finite state machines and statecharts for the modern web.
š Read the documentation
š Adheres to the SCXML specification.
Packages
Super quick start
npm install xstate
import { createMachine, interpret } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const toggleService = interpret(toggleMachine)
.onTransition((state) => console.log(state.value))
.start();
toggleService.send({ type: 'TOGGLE' });
toggleService.send({ type: 'TOGGLE' });
Visualizer
Visualize, simulate, and share your statecharts in XState Viz!
Why?
Statecharts are a formalism for modeling stateful, reactive systems. This is useful for declaratively describing the behavior of your application, from the individual components to the overall application logic.
Read š½ the slides (š„ video) or check out these resources for learning about the importance of finite state machines and statecharts in user interfaces:
Finite State Machines
import { createMachine } from 'xstate';
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow'
}
},
yellow: {
on: {
TIMER: 'red'
}
},
red: {
on: {
TIMER: 'green'
}
}
}
});
const currentState = 'green';
const nextState = lightMachine.transition(currentState, { type: 'TIMER' })
.value;
Hierarchical (Nested) State Machines
import { createMachine } from 'xstate';
const pedestrianStates = {
initial: 'walk',
states: {
walk: {
on: {
PED_TIMER: 'wait'
}
},
wait: {
on: {
PED_TIMER: 'stop'
}
},
stop: {}
}
};
const lightMachine = createMachine({
id: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow'
}
},
yellow: {
on: {
TIMER: 'red'
}
},
red: {
on: {
TIMER: 'green'
},
...pedestrianStates
}
}
});
const currentState = 'yellow';
const nextState = lightMachine.transition(currentState, { type: 'TIMER' })
.value;
lightMachine.transition('red.walk', { type: 'PED_TIMER' }).value;
Object notation for hierarchical states:
const waitState = lightMachine.transition(
{ red: 'walk' },
{ type: 'PED_TIMER' }
).value;
lightMachine.transition(waitState, { type: 'PED_TIMER' }).value;
lightMachine.transition({ red: 'stop' }, { type: 'TIMER' }).value;
Parallel State Machines
const wordMachine = createMachine({
id: 'word',
type: 'parallel',
states: {
bold: {
initial: 'off',
states: {
on: {
on: { TOGGLE_BOLD: 'off' }
},
off: {
on: { TOGGLE_BOLD: 'on' }
}
}
},
underline: {
initial: 'off',
states: {
on: {
on: { TOGGLE_UNDERLINE: 'off' }
},
off: {
on: { TOGGLE_UNDERLINE: 'on' }
}
}
},
italics: {
initial: 'off',
states: {
on: {
on: { TOGGLE_ITALICS: 'off' }
},
off: {
on: { TOGGLE_ITALICS: 'on' }
}
}
},
list: {
initial: 'none',
states: {
none: {
on: { BULLETS: 'bullets', NUMBERS: 'numbers' }
},
bullets: {
on: { NONE: 'none', NUMBERS: 'numbers' }
},
numbers: {
on: { BULLETS: 'bullets', NONE: 'none' }
}
}
}
}
});
const boldState = wordMachine.transition('bold.off', { type: 'TOGGLE_BOLD' })
.value;
const nextState = wordMachine.transition(
{
bold: 'off',
italics: 'off',
underline: 'on',
list: 'bullets'
},
{ type: 'TOGGLE_ITALICS' }
).value;
History States
const paymentMachine = createMachine({
id: 'payment',
initial: 'method',
states: {
method: {
initial: 'cash',
states: {
cash: { on: { SWITCH_CHECK: 'check' } },
check: { on: { SWITCH_CASH: 'cash' } },
hist: { type: 'history' }
},
on: { NEXT: 'review' }
},
review: {
on: { PREVIOUS: 'method.hist' }
}
}
});
const checkState = paymentMachine.transition('method.cash', {
type: 'SWITCH_CHECK'
});
const reviewState = paymentMachine.transition(checkState, { type: 'NEXT' });
const previousState = paymentMachine.transition(reviewState, {
type: 'PREVIOUS'
}).value;
Huge thanks to the following companies for sponsoring xstate
. You can sponsor further xstate
development on OpenCollective.