XState
JavaScript state machines and statecharts

npm version Statecharts gitter chat Packages - 图4

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

📖 Read the documentation 📑 Adheres to the SCXML specification.

Packages

Super quick start

  1. npm install xstate
  1. import { createMachine, interpret } from 'xstate';
  2. // Stateless machine definition
  3. // machine.transition(...) is a pure function used by the interpreter.
  4. const toggleMachine = createMachine({
  5. id: 'toggle',
  6. initial: 'inactive',
  7. states: {
  8. inactive: { on: { TOGGLE: 'active' } },
  9. active: { on: { TOGGLE: 'inactive' } }
  10. }
  11. });
  12. // Machine instance with internal state
  13. const toggleService = interpret(toggleMachine)
  14. .onTransition((state) => console.log(state.value))
  15. .start();
  16. // => 'inactive'
  17. toggleService.send('TOGGLE');
  18. // => 'active'
  19. toggleService.send('TOGGLE');
  20. // => 'inactive'

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

  1. import { createMachine } from 'xstate';
  2. const lightMachine = createMachine({
  3. id: 'light',
  4. initial: 'green',
  5. states: {
  6. green: {
  7. on: {
  8. TIMER: 'yellow'
  9. }
  10. },
  11. yellow: {
  12. on: {
  13. TIMER: 'red'
  14. }
  15. },
  16. red: {
  17. on: {
  18. TIMER: 'green'
  19. }
  20. }
  21. }
  22. });
  23. const currentState = 'green';
  24. const nextState = lightMachine.transition(currentState, 'TIMER').value;
  25. // => 'yellow'

Hierarchical (Nested) State Machines

Hierarchical Light Machine

  1. import { createMachine } from 'xstate';
  2. const pedestrianStates = {
  3. initial: 'walk',
  4. states: {
  5. walk: {
  6. on: {
  7. PED_TIMER: 'wait'
  8. }
  9. },
  10. wait: {
  11. on: {
  12. PED_TIMER: 'stop'
  13. }
  14. },
  15. stop: {}
  16. }
  17. };
  18. const lightMachine = createMachine({
  19. id: 'light',
  20. initial: 'green',
  21. states: {
  22. green: {
  23. on: {
  24. TIMER: 'yellow'
  25. }
  26. },
  27. yellow: {
  28. on: {
  29. TIMER: 'red'
  30. }
  31. },
  32. red: {
  33. on: {
  34. TIMER: 'green'
  35. },
  36. ...pedestrianStates
  37. }
  38. }
  39. });
  40. const currentState = 'yellow';
  41. const nextState = lightMachine.transition(currentState, 'TIMER').value;
  42. // => {
  43. // red: 'walk'
  44. // }
  45. lightMachine.transition('red.walk', 'PED_TIMER').value;
  46. // => {
  47. // red: 'wait'
  48. // }

Object notation for hierarchical states:

  1. // ...
  2. const waitState = lightMachine.transition({ red: 'walk' }, 'PED_TIMER').value;
  3. // => { red: 'wait' }
  4. lightMachine.transition(waitState, 'PED_TIMER').value;
  5. // => { red: 'stop' }
  6. lightMachine.transition({ red: 'stop' }, 'TIMER').value;
  7. // => 'green'

Parallel State Machines

Parallel state machine

  1. const wordMachine = createMachine({
  2. id: 'word',
  3. type: 'parallel',
  4. states: {
  5. bold: {
  6. initial: 'off',
  7. states: {
  8. on: {
  9. on: { TOGGLE_BOLD: 'off' }
  10. },
  11. off: {
  12. on: { TOGGLE_BOLD: 'on' }
  13. }
  14. }
  15. },
  16. underline: {
  17. initial: 'off',
  18. states: {
  19. on: {
  20. on: { TOGGLE_UNDERLINE: 'off' }
  21. },
  22. off: {
  23. on: { TOGGLE_UNDERLINE: 'on' }
  24. }
  25. }
  26. },
  27. italics: {
  28. initial: 'off',
  29. states: {
  30. on: {
  31. on: { TOGGLE_ITALICS: 'off' }
  32. },
  33. off: {
  34. on: { TOGGLE_ITALICS: 'on' }
  35. }
  36. }
  37. },
  38. list: {
  39. initial: 'none',
  40. states: {
  41. none: {
  42. on: { BULLETS: 'bullets', NUMBERS: 'numbers' }
  43. },
  44. bullets: {
  45. on: { NONE: 'none', NUMBERS: 'numbers' }
  46. },
  47. numbers: {
  48. on: { BULLETS: 'bullets', NONE: 'none' }
  49. }
  50. }
  51. }
  52. }
  53. });
  54. const boldState = wordMachine.transition('bold.off', 'TOGGLE_BOLD').value;
  55. // {
  56. // bold: 'on',
  57. // italics: 'off',
  58. // underline: 'off',
  59. // list: 'none'
  60. // }
  61. const nextState = wordMachine.transition(
  62. {
  63. bold: 'off',
  64. italics: 'off',
  65. underline: 'on',
  66. list: 'bullets'
  67. },
  68. 'TOGGLE_ITALICS'
  69. ).value;
  70. // {
  71. // bold: 'off',
  72. // italics: 'on',
  73. // underline: 'on',
  74. // list: 'bullets'
  75. // }

History States

Machine with history state

  1. const paymentMachine = createMachine({
  2. id: 'payment',
  3. initial: 'method',
  4. states: {
  5. method: {
  6. initial: 'cash',
  7. states: {
  8. cash: { on: { SWITCH_CHECK: 'check' } },
  9. check: { on: { SWITCH_CASH: 'cash' } },
  10. hist: { type: 'history' }
  11. },
  12. on: { NEXT: 'review' }
  13. },
  14. review: {
  15. on: { PREVIOUS: 'method.hist' }
  16. }
  17. }
  18. });
  19. const checkState = paymentMachine.transition('method.cash', 'SWITCH_CHECK');
  20. // => State {
  21. // value: { method: 'check' },
  22. // history: State { ... }
  23. // }
  24. const reviewState = paymentMachine.transition(checkState, 'NEXT');
  25. // => State {
  26. // value: 'review',
  27. // history: State { ... }
  28. // }
  29. const previousState = paymentMachine.transition(reviewState, 'PREVIOUS').value;
  30. // => { method: 'check' }

Sponsors

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

Packages - 图10 Packages - 图11