Stately
XState v6 alpha

Transitions

Configure event, delayed, eventless and completion transitions.

XState v6 is in alpha

APIs and behavior may change before the stable release.

A transition describes how a state responds to an event.

idle: { on: { start: { target: 'active' } } }

Transition properties

PropertyDescription
targetTarget state or states.
matchesEvent payload that must match.
contextContext patch or mapper.
inputInput for the target state.
reenterRe-enter the source state when targeting it.
metaPer-transition metadata.
descriptionHuman-readable description.

There is no guard property. Conditions live inside the transition function, which returns undefined to reject the event. See guards.

A targetless transition can update context and run effects without leaving the current state. Set reenter: true when a self-transition should run exit and entry behavior again.

on: {
  rename: ({ context, event }) => ({
    context: { ...context, name: event.name }
  }),
  restart: { target: 'active', reenter: true }
}

Transition functions

submit: ({ context, event }, enq) => {
  if (!context.valid) return;
  enq(() => console.log('Submitted', event));
  return {
    target: 'submitting',
    context: { ...context, submittedAt: Date.now() }
  };
}

Returning undefined prevents the transition.

always runs without an external event. after runs after a delay. onDone, onError and onTimeout handle actor outcomes.

Use targetless transitions for edits that keep a form on the same step. Use re-entering transitions to restart a timer, subscription or invoked request.

Put shared transitions on a parent state. A child can set an event to undefined to forbid that parent transition. Wildcards such as pointer.* match an event family when no exact transition matches.

on: {
  'pointer.*': { target: 'tracking' },
  '*': { target: 'unexpectedEvent' }
}

Match event payloads

Internal lifecycle events use stable category types and carry the identity of what produced them:

Event typeIdentity
xstate.done.actoractorId, sessionId
xstate.error.actoractorId, sessionId
xstate.timeout.actoractorId, sessionId
xstate.done.statestateId
xstate.afterstateId, delay
xstate.timeoutstateId

Use matches to select one payload of an event type:

on: {
  'xstate.done.actor': {
    matches: { actorId: 'job' },
    target: 'complete'
  }
}

matches is a shallow partial pattern over the event's payload, compared by identity, so use it with primitive values. It is checked before the transition function runs. It works on any event, not only lifecycle events. onDone, onError, onTimeout and after set matches for you, which is how each one selects its own actor or state.

One transition per event

Transition arrays are not accepted by the authoring APIs. An event maps to a single transition. Return a target from a transition function to choose among several, and use matches to select a payload. Serialized transition arrays are still accepted by createMachineFromConfig(...).

on: {
  submit: ({ context }) =>
    context.role === 'admin'
      ? { target: 'adminReview' }
      : { target: 'standardReview' }
}

TypeScript

Transition targets are checked against authored state paths. Event schemas narrow event inside transition functions.

Transitions cheatsheet

on: { submit: { target: 'loading' } }
on: { rename: ({ context, event }) => ({ context: { ...context, name: event.name } }) }
on: { cancel: undefined }
always: { target: 'ready' }
after: { 1000: { target: 'idle' } }
on: { 'xstate.done.actor': { matches: { actorId: 'job' }, target: 'done' } }
onDone: { target: 'success' }
onError: { target: 'failure' }

On this page