Snapshot
machine.getSnapshot() returns the complete immutable read model for one point in time.
Shared shape
Section titled “Shared shape”const snapshot = machine.getSnapshot();
snapshot.type; // "linear" | "graph"snapshot.status; // "idle" | "running" | "paused" | "completed" | "terminated"snapshot.context;snapshot.currentStep;snapshot.transition;snapshot.history;snapshot.machine;snapshot.plugins;Current step
Section titled “Current step”currentStep is null before initial entry. Otherwise it contains:
| Field | Meaning |
|---|---|
id | Current step id. |
metadata | Static definition metadata. |
isFirstTimeVisit | true only on the first entry of this step in the run. |
async | Loading, success, and error state for current entry work. |
Linear current steps add index, isFirstStep, and isLastStep. Graph current steps add
isTerminal.
Transition
Section titled “Transition”snapshot.transition = { pending: false, phase: null, // "working" | "leaving" | "entering" | null from: null, to: null};History
Section titled “History”snapshot.history = { timeline: ["account", "review"], currentIndex: 1, visited: { account: true, review: true }, canGoBack: true, canGoForward: false};visited has an entry for every declared step.
Machine state
Section titled “Machine state”snapshot.machine carries one field, outcome — how the run ended, once it has.
It used to carry six more: isLoading and one boolean per status. Every one of them restated
something the snapshot already said, so they are gone. Read snapshot.status directly for the
lifecycle ("idle" | "running" | "paused" | "completed" | "terminated"), and
snapshot.transition.pending for “is a move in flight” — transition also tells you the phase
and the from/to of that move, which a bare boolean could not. snapshot.currentStep.async is
the separate, step-level question: whether the current entry’s own work settled or failed.
snapshot.machine.outcome = null; // or { type: "completed" | "terminated", payload }Completion and termination set snapshot.machine.outcome; only restart() clears it back to null.
Linear snapshot
Section titled “Linear snapshot”if (snapshot.type === "linear") { snapshot.steps.stepOrder; snapshot.steps.totalSteps; snapshot.steps.visitedStepCount; snapshot.currentStep?.isLastStep;}Graph snapshot
Section titled “Graph snapshot”if (snapshot.type === "graph") { snapshot.declaredEvents; snapshot.availableEvents; snapshot.availableSteps; snapshot.outgoingTransitions; snapshot.steps.totalSteps; snapshot.steps.visitedStepCount; snapshot.currentStep?.isTerminal;}declaredEvents includes every event declared from the current step. availableEvents and
availableSteps include only candidates whose guard currently passes. outgoingTransitions
explains both projections with each candidate’s target, priority, guard result, enabled state, and
whether first-enabled event dispatch would select it. A terminal step has no declared outgoing
transitions, regardless of guard results.
Introspection shows the resting-state answer. A
work send’s candidate guard that reads the run
result is evaluated here with result: undefined — outside a send there is no result yet. During
the send itself the same guard sees the live result, so a result-dependent candidate can report
guard: "failed" in the snapshot and still win the route once the work has run.
Update rules
Section titled “Update rules”Do not mutate a snapshot or its context. Use machine.context.update() and read the next snapshot.
Read the slice a consumer needs inside the listener:
machine.subscriptions.subscribe(() => renderAsyncState(machine.getSnapshot().currentStep?.async));