Getting Started with XState

Benjamin Franklin
6 min readFeb 27, 2023

XState is a powerful JavaScript library for managing state in complex applications. It provides a declarative way to define finite state machines and state charts, which can help simplify complex application logic and improve code maintainability. In this article, we will explore how to use XState in your JavaScript applications.

1. Installing XState

To get started with XState, you’ll need to install it via NPM or Yarn. You can do this by running the following command:

npm install xstate

2. Creating a Finite State Machine

A Finite State Machine (FSM) is a mathematical model that describes a system that can be in one of a finite number of states, and can transition between those states based on certain inputs. In XState, you can define an FSM using the createMachine function. Here's an example of a simple FSM that represents a light bulb:

import { createMachine } from 'xstate';

const lightBulbMachine = createMachine({
id: 'lightBulb',
initial: 'off',
states: {
off: {
on: {
TOGGLE: 'on',
},
},
on: {
on: {
TOGGLE: 'off',
},
},
},
});j

In this example, we define an FSM with two states, off and on. The initial state is off. The on state has a transition that is triggered by the TOGGLE event, which transitions the machine to the off state.

3. Creating a State Chart

A State Chart is a hierarchical Finite State Machine that provides a way to break down complex state logic into smaller, more manageable pieces. In XState, you can define a State Chart using the createMachine function, just like an FSM. Here's an example of a State Chart that represents a traffic light:

import { createMachine } from 'xstate';

const trafficLightMachine = createMachine({
id: 'trafficLight',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow',
},
},
yellow: {
on: {
TIMER: 'red',
},
},
red: {
on: {
TIMER: 'green',
},
},
},
});

In this example, we define a State Chart with three states, green, yellow, and red. The initial state is green. Each state has a transition that is triggered by the TIMER event, which transitions the machine to the next state in the sequence.

4. Using the State Machine or State Chart in Your Application

Once you’ve defined your FSM or State Chart, you can use it in your application to manage state. In XState, you can use the interpret function to create an interpreter for your machine. The interpreter manages the state of your machine and provides methods for sending events and getting the current state. Here's an example of how to use the interpret function with the lightBulbMachine we defined earlier:

import { interpret } from 'xstate';

const lightBulbService = interpret(lightBulbMachine);

lightBulbService.start();

lightBulbService.send('TOGGLE');

console.log(lightBulbService.state.value); // 'on'

In this example, we create an interpreter for the lightBulbMachine and start it. We then send the TOGGLE event to the machine, which transitions it to the on state. Finally, we log the current state of the machine, which is on.

5. Adding Actions to Transitions

Transitions in XState can also perform actions, which are functions that are executed when a transition occurs. Actions can be used to perform side effects, update application state, or dispatch additional events. Here’s an example of how to add an action to a transition in XState:

import { createMachine } from 'xstate';

const lightBulbMachine = createMachine({
id: 'lightBulb',
initial: 'off',
states: {
off: {
on: {
TOGGLE: {
target: 'on',
actions: ['turnOn'],
},
},
},
on: {
on: {
TOGGLE: {
target: 'off',
actions: ['turnOff'],
},
},
},
},
}, {
actions: {
turnOn: () => {
console.log('Turning light on...');
},
turnOff: () => {
console.log('Turning light off...');
},
},
});

In this example, we define two actions, turnOn and turnOff, and add them to the TOGGLE transitions in the off and on states, respectively. When the TOGGLE event is sent to the machine and the transition occurs, the corresponding action will be executed.

6. Adding Conditions to Transitions

Transitions in XState can also have conditions, which are boolean expressions that are evaluated before a transition occurs. Conditions can be used to control whether a transition should occur based on the current state of the machine or some other application state. Here’s an example of how to add a condition to a transition in XState:

import { createMachine } from 'xstate';

const lightBulbMachine = createMachine({
id: 'lightBulb',
initial: 'off',
states: {
off: {
on: {
TOGGLE: [
{
target: 'on',
cond: 'isDaytime',
},
{
target: 'off',
cond: 'isNighttime',
},
],
},
},
on: {
on: {
TOGGLE: [
{
target: 'off',
cond: 'isDaytime',
},
{
target: 'on',
cond: 'isNighttime',
},
],
},
},
},
}, {
guards: {
isDaytime: () => {
const hour = new Date().getHours();
return hour >= 6 && hour < 18;
},
isNighttime: () => {
const hour = new Date().getHours();
return hour < 6 || hour >= 18;
},
},
});

In this example, we define two guards, isDaytime and isNighttime, and add them to the TOGGLE transitions in the off and on states, respectively. The guards evaluate the current time and return true or false based on whether it is daytime or nighttime. If it is daytime, the machine transitions to the on state; if it is nighttime, the machine transitions to the off state.

Conclusion

XState is a powerful library for managing state in complex applications. It provides a declarative way to define finite state machines and state charts, which can help simplify complex application logic and improve code maintainability. With XState, you can define your application logic in a way that is easy to understand and reason about, and use the interpreter to manage state and respond to events. Whether you’re building a simple UI component or a large-scale application, XState can help you manage state in a more intuitive and scalable way.

Some additional tips for using XState effectively:

  1. Start small and build up: When working with XState, it’s often helpful to start with a small finite state machine and add complexity gradually. This can help you avoid getting overwhelmed by the complexity of your application logic.
  2. Use nested state charts: XState allows you to nest statecharts inside other statecharts, which can help you manage complexity and keep your code organized. For example, you might have a statechart for managing the state of a single component, and another statechart for managing the state of the entire application.
  3. Use actions sparingly: While actions can be useful for performing side effects or updating application state, they can also make your state machine more complex and harder to reason about. Whenever possible, try to keep your actions simple and focused.
  4. Use guards to manage conditions: Guards are a powerful feature of XState that allow you to manage conditions for transitions. By using guards effectively, you can make your state machine more flexible and better able to handle edge cases.
  5. Use the devtools: XState comes with a powerful set of devtools that can help you debug and understand the behavior of your state machine. Take advantage of these tools to help you troubleshoot problems and optimize performance.

In conclusion, XState is a powerful tool for managing state in complex applications. By using XState to define your application logic as finite state machines and statecharts, you can simplify your code and improve code maintainability. With XState, you can build scalable, maintainable applications that are easier to understand and reason about.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Benjamin Franklin
Benjamin Franklin

Written by Benjamin Franklin

Enthusiastic software engineer & seasoned traveller who likes to dabble in a lot of different things.

No responses yet

Write a response