Parallel States
A state can declare parallel regions — a set of independent sub-machines that all become active when the parent state is entered and are destroyed when it is left.
const editor = defineMachine({
id: 'editor',
initial: 'editing',
states: {
editing: {
parallel: {
saving: {
initial: 'idle',
states: {
idle: { on: { START_SAVE: { target: 'saving' } } },
saving: { on: { SAVE_DONE: { target: 'saved' } } },
saved: {},
},
},
validation: {
initial: 'valid',
states: {
valid: { on: { INVALIDATE: { target: 'invalid' } } },
invalid: { on: { VALIDATE: { target: 'valid' } } },
},
},
},
},
idle: {},
},
})send() delivers every event to all active regions. Each region handles it independently.
const { matches, send } = useMachine(editor)
matches('editing') // main state
matches({ saving: 'idle' }) // region check
matches({ validation: 'valid' }) // another region
await send('INVALIDATE')
matches({ validation: 'invalid' }) // true
matches({ saving: 'idle' }) // still true — unaffectedContext conflict resolution: each region's transition only ever reports the fields its own actions actually touched — an untouched field is never overwritten by a sibling region's stale copy of it. When two regions do return a Partial<context> that touches the same field, the last region in declaration order wins. A console.warn is emitted in dev mode naming the conflicting regions and field.
Limitation: parallel regions support one level of nesting. Regions cannot themselves contain parallel. This is a deliberate choice to control complexity.