Skip to content
State machines and statecharts for the modern web.
TypeScript JavaScript Other
Branch: master
Clone or download

Latest commit

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.changeset Version Packages May 20, 2020
.codesandbox Add CodeSandbox CI Config Dec 8, 2019
.github Update bug_report.md May 29, 2020
.vscode chore: don't ignore settings.json Jul 15, 2019
docs Add callout to main README.md Jun 2, 2020
packages Clean up asEffect, asLayoutEffect May 29, 2020
patches Bring back @changesets/assemble-release-plan patch Apr 4, 2020
scripts Run prettier on the codebase Apr 4, 2020
.gitignore Migrate to Jest Aug 2, 2019
.prettierrc Prettier + adding unit tests for guard conditions Dec 6, 2017
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md Nov 24, 2018
CONTRIBUTING.md Update CONTRIBUTING.md to add instructions for creating a changeset Dec 30, 2019
ISSUE_TEMPLATE.md Add link to CodeSandbox template in the ISSUE_TEMPLATE Jul 31, 2019
LICENSE Initial commit Sep 14, 2015
README.md Add callout to main README.md Jun 2, 2020
jest.config.js Fix workspaces setup, exclude @xstate/devtools from workspace package… Dec 30, 2019
lerna.json Migrate to Yarn & move core package to separate directory Oct 5, 2019
migration.md Create migration.md Nov 1, 2018
package.json Fix typedoc Apr 18, 2020
tsconfig.base.json chore: add skipLibCheck: true to tsconfig.base.json Nov 28, 2019
tsconfig.monorepo.json chore(build): fix reference to types file in @xstate/fsm Nov 5, 2019
tslint.json Add withServiceScope Apr 28, 2019
yarn.lock Fix typedoc Apr 18, 2020

README.md


XState
JavaScript state machines and statecharts

npm version Statecharts gitter chat

Black lives matter. Support the Equal Justice Initiative. ✊🏽✊🏾✊🏿

JavaScript and TypeScript finite state machines and statecharts for the modern web.

📖 Read the documentation 📑 Adheres to the SCXML specification.

Packages

  • 🤖 xstate - Core finite state machine and statecharts library + interpreter
  • 🔬 @xstate/fsm - Minimal finite state machine library
  • 📉 @xstate/graph - Graph traversal utilities for XState
  • ⚛️ @xstate/react - React hooks and utilities for using XState in React applications
  • 💚 @xstate/vue - Vue composition functions and utilities for using XState in Vue applications
  • @xstate/test - Model-based testing utilities for XState

Templates

Get started by forking one of these templates on CodeSandbox:

Super quick start

npm install xstate
import { createMachine, interpret } from 'xstate';

// Stateless machine definition
// machine.transition(...) is a pure function used by the interpreter.
const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } }
  }
});

// Machine instance with internal state
const toggleService = interpret(toggleMachine)
  .onTransition((state) => console.log(state.value))
  .start();
// => 'inactive'

toggleService.send('TOGGLE');
// => 'active'

toggleService.send('TOGGLE');
// => 'inactive'

Promise example

📉 See the visualization on xstate.js.org/viz

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

const fetchMachine = createMachine({
  id: 'SWAPI',
  initial: 'idle',
  context: {
    user: null
  },
  states: {
    idle: {
      on: {
        FETCH: 'loading'
      }
    },
    loading: {
      invoke: {
        id: 'fetchLuke',
        src: (context, event) =>
          fetch('https://swapi.dev/api/people/1').then((res) => res.data),
        onDone: {
          target: 'resolved',
          actions: assign({
            user: (_, event) => event.data
          })
        },
        onError: 'rejected'
      },
      on: {
        CANCEL: 'idle'
      }
    },
    resolved: {
      type: 'final'
    },
    rejected: {
      on: {
        FETCH: 'loading'
      }
    }
  }
});

const swService = interpret(fetchMachine)
  .onTransition((state) => console.log(state.value))
  .start();

swService.send('FETCH');

Visualizer

Visualize, simulate, and share your statecharts in XState Viz!

xstate visualizer

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

Light Machine

import { Machine } from 'xstate';

const lightMachine = Machine({
  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, 'TIMER').value;

// => 'yellow'

Hierarchical (Nested) State Machines

Hierarchical Light Machine

import { Machine } from 'xstate';

const pedestrianStates = {
  initial: 'walk',
  states: {
    walk: {
      on: {
        PED_TIMER: 'wait'
      }
    },
    wait: {
      on: {
        PED_TIMER: 'stop'
      }
    },
    stop: {}
  }
};

const lightMachine = Machine({
  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, 'TIMER').value;
// => {
//   red: 'walk'
// }

lightMachine.transition('red.walk', 'PED_TIMER').value;
// => {
//   red: 'wait'
// }

Object notation for hierarchical states:

// ...
const waitState = lightMachine.transition({ red: 'walk' }, 'PED_TIMER').value;

// => { red: 'wait' }

lightMachine.transition(waitState, 'PED_TIMER').value;

// => { red: 'stop' }

lightMachine.transition({ red: 'stop' }, 'TIMER').value;

// => 'green'

Parallel State Machines

Parallel state machine

const wordMachine = Machine({
  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', 'TOGGLE_BOLD').value;

// {
//   bold: 'on',
//   italics: 'off',
//   underline: 'off',
//   list: 'none'
// }

const nextState = wordMachine.transition(
  {
    bold: 'off',
    italics: 'off',
    underline: 'on',
    list: 'bullets'
  },
  'TOGGLE_ITALICS'
).value;

// {
//   bold: 'off',
//   italics: 'on',
//   underline: 'on',
//   list: 'bullets'
// }

History States

Machine with history state

const paymentMachine = Machine({
  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', 'SWITCH_CHECK');

// => State {
//   value: { method: 'check' },
//   history: State { ... }
// }

const reviewState = paymentMachine.transition(checkState, 'NEXT');

// => State {
//   value: 'review',
//   history: State { ... }
// }

const previousState = paymentMachine.transition(reviewState, 'PREVIOUS').value;

// => { method: 'check' }

Sponsors

Huge thanks to the following companies for sponsoring xstate. You can sponsor further xstate development on OpenCollective.

You can’t perform that action at this time.