Skip to content
Version: XState v4

Advanced transitions

Parent and child states offer more opportunities for various types of transitions. You can transition from any state to any other state. XState has a few different syntaxes to help you out.

Transitioning to a node’s own children

Use the .target syntax to transition to a child state from a parent state.

The following machine uses the .target syntax for .hovered:

import { createMachine } from 'xstate';

const machine = createMachine({
initial: 'inactive',
on: {
HOVER: '.hovered',
},
states: {
inactive: {},
hovered: {},
visited: {},
},
});

Whichever state the machine is in, whenever it receives a HOVER event, it’ll transition to the hovered state.

Transitioning to another node’s children

Transition directly into a node’s children using the a.b syntax.

The following machine uses the a.b syntax for .hovered:

import { createMachine } from 'xstate';

const machine = createMachine({
initial: 'inactive',
states: {
inactive: {
on: {
HOVER: 'active.hovered',
},
},
active: {
initial: 'focused',
states: {
focused: {},
hovered: {},
},
},
},
});

In the example above, the machine transitions directly to active.hovered when the HOVER event is received in state inactive.

The initial state of focused inside active is ignored.

Transitioning to any node

If you want to transition to any state:

  1. Give the state node an id, for example, myState
  2. Target the state node with a # prefix: #myState

The approach above works for transitioning to any state node from any state node, so it’s a useful trick if you can’t target a state with the .a or a.b syntaxes.

The example below shows the paused state with an id of playerPaused and that id targeted using #playerPaused:

import { createMachine } from 'xstate';

const musicMachine = createMachine({
initial: 'playing',
states: {
playing: {
initial: 'playingTrackOne',
states: {
playingTrackOne: {
on: {
PAUSE: {
target: '#playerPaused',
},
},
},
},
},
paused: {
id: 'playerPaused',
},
},
});

You can also give the root of your machine an id and combine the use of the root machine’s id with the a.b syntax.

The example below shows the machine’s root with an id of player and the paused state targeted using #player.paused:

import { createMachine } from 'xstate';

const musicMachine = createMachine({
id: 'player',
initial: 'playing',
states: {
playing: {
initial: 'playingTrackOne',
states: {
playingTrackOne: {
on: {
PAUSE: {
target: '#player.paused',
},
},
},
},
},
paused: {},
},
});

Combining a machine’s root id with the use of the a.b syntax can help you reduce the number of state ids required.