Stately
State Machines

Final states

A final state is a state that represents the completion or successful termination of a machine. It is defined by the type: 'final' property on a state node:

import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      /* ... */
    },
    thanks: {
      /* ... */
    },
    closed: {
      type: 'final',
    },
    // ...
  },
  on: {
    'feedback.close': {
      target: '.closed',
    },
  },
});

When a machine reaches the final state, it can no longer receive any events, and anything running inside it is canceled and cleaned up. The box with a surrounding border icon represents the final state.

A machine can have multiple final states or no final states.

  • A state machine can have zero or more final states. Some machines may run indefinitely and not need to terminate.
  • Final states can have output data, which is sent to the parent machine when the machine terminates.
  • When a machine reaches a top-level final state, it terminates.
  • Final states cannot have transitions

Top-level final states

A top-level final state is a final state that is a direct child state of the machine. When the machine reaches a top-level final state, the machine will terminate. When a machine terminates, it can no longer receive events nor transition.

Child final states

When a child final state of a parent (compound) state is reached, that parent state is considered "done". The onDone transition of that parent state is automatically taken.

import { createMachine } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      initial: 'weighing',
      states: {
        weighing: {
          on: {
            weighed: {
              target: 'grinding',
            },
          },
        },
        grinding: {
          on: {
            ground: 'ready',
          },
        },
        ready: {
          // Child final state of parent state 'preparation'
          type: 'final',
        },
      },
      // Transition will be taken when child final state is reached
      onDone: {
        target: 'brewing',
      },
    },
    brewing: {
      // ...
    },
  },
});

Final states in parallel states

When all regions of a parallel state are "done", the parallel state is considered "done". The onDone transition of the parallel state is taken.

In this example, the preparation state is a parallel state with two regions: beans and water. When both regions are done, the preparation state is done, and the brewing state is entered.

import { createMachine, createActor } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      type: 'parallel',
      states: {
        beans: {
          initial: 'grinding',
          states: {
            grinding: {
              on: {
                grindingComplete: 'ground',
              },
            },
            ground: {
              type: 'final',
            },
          },
        },
        water: {
          initial: 'heating',
          states: {
            heating: {
              always: {
                guard: 'waterBoiling',
                target: 'heated',
              },
            },
            heated: {
              type: 'final',
            },
          },
        },
      },
      onDone: 'brewing',
    },
    brewing: {},
  },
});

Output

When a machine reaches its top-level final state, it can produce output data. You can specify this output data in the .output property of the machine config:

import { createMachine, createActor } from 'xstate';

const currencyMachine = createMachine({
  // ...
  states: {
    converting: {
      // ...
    },
    converted: {
      type: 'final',
    },
  },
  output: ({ context }) => ({
    amount: context.amount,
    currency: context.currency,
  }),
});

const currencyActor = createActor(currencyMachine, {
  input: {
    amount: 10,
    fromCurrency: 'USD',
    toCurrency: 'EUR',
  },
});

currencyActor.subscribe({
  complete() {
    console.log(currencyActor.getSnapshot().output);
    // logs e.g. { amount: 12, currency: 'EUR' }
  },
});

The .output property can also be a static value:

import { createMachine, createActor } from 'xstate';

const processMachine = createMachine({
  // ...
  output: {
    message: 'Process completed.',
  },
});

Final states cheatsheet

import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      /* ... */
    },
    thanks: {
      /* ... */
    },
    closed: {
      type: 'final',
    },
    // ...
  },
  on: {
    'feedback.close': {
      target: '.closed',
    },
  },
});

Cheatsheet: final states in parallel states

import { createMachine } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      type: 'parallel',
      states: {
        beans: {
          initial: 'grinding',
          states: {
            grinding: {
              on: {
                grindingComplete: 'ground',
              },
            },
            ground: {
              type: 'final',
            },
          },
        },
        water: {
          initial: 'heating',
          states: {
            heating: {
              always: {
                guard: 'waterBoiling',
                target: 'heated',
              },
            },
            heated: {
              type: 'final',
            },
          },
        },
      },
      onDone: 'brewing',
    },
    brewing: {},
  },
});

On this page