# Hierarchical state nodes
These XState v4 docs are no longer maintained
XState v5 is out now! Read more about XState v5 (opens new window)
🆕 Find more about parent and child states in XState (opens new window) as well as a no-code introduction to parent states (opens new window).
In statecharts, states can be nested within other states. These nested states are called compound states. To learn more, read the compound states section in our introduction to statecharts.
# API
The following example is a traffic light machine with nested states:
const pedestrianStates = {
initial: 'walk',
states: {
walk: {
on: {
PED_COUNTDOWN: { target: 'wait' }
}
},
wait: {
on: {
PED_COUNTDOWN: { target: 'stop' }
}
},
stop: {},
blinking: {}
}
};
const lightMachine = createMachine({
key: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: { target: 'yellow' }
}
},
yellow: {
on: {
TIMER: { target: 'red' }
}
},
red: {
on: {
TIMER: { target: 'green' }
},
...pedestrianStates
}
},
on: {
POWER_OUTAGE: { target: '.red.blinking' },
POWER_RESTORED: { target: '.red' }
}
});
The 'green'
and 'yellow'
states are simple states - they have no child states. In contrast, the 'red'
state is a compound state since it is composed of substates (the pedestrianStates
).
# Initial states
When a compound state is entered, its initial state is immediately entered as well. In the following traffic light machine example:
- the
'red'
state is entered - since
'red'
has an initial state of'walk'
, the{ red: 'walk' }
state is ultimately entered.
console.log(lightMachine.transition('yellow', { type: 'TIMER' }).value);
// => {
// red: 'walk'
// }
# Events
When a simple state does not handle an event
, that event
is propagated up to its parent state to be handled. In the following traffic light machine example:
- the
{ red: 'stop' }
state does not handle the'TIMER'
event - the
'TIMER'
event is sent to the'red'
parent state, which handles the event.
console.log(lightMachine.transition({ red: 'stop' }, { type: 'TIMER' }).value);
// => 'green'
If neither a state nor any of its ancestor (parent) states handle an event, no transition happens. In strict
mode (specified in the machine configuration), this will throw an error.
console.log(lightMachine.transition('green', { type: 'UNKNOWN' }).value);
// => 'green'