Stately
XState v6 alpha

Actor snapshots

Read and observe actor state.

XState v6 is in alpha

APIs and behavior may change before the stable release.

An actor snapshot describes the actor at one point in time.

const subscription = actor.subscribe((snapshot) => {
  console.log(snapshot.status);
});

actor.start();
subscription.unsubscribe();

getSnapshot() reads the current snapshot without subscribing. subscribe(...) observes future snapshots. A subscription created before start() also receives the initial snapshot.

Use a snapshot to render a checkout step or upload progress. Machine snapshots also carry value, context, children and helpers such as matches(...) and can(...); see states for the full member list.

Status

Every snapshot has a status.

StatusMeaningSet by
activeThe actor is running and can receive events.Creating and starting the actor.
doneThe actor completed. output is set.Reaching a top-level final state, or the logic resolving.
errorThe actor failed. error is set.An uncaught error in a transition function, action or invoked actor.
stoppedThe actor was stopped before completing.actor.stop(), or its parent stopping it when a state is exited.

done, error and stopped are terminal: the actor no longer processes events, its children and timers are disposed, and its observers are completed or errored.

const snapshot = actor.getSnapshot();

if (snapshot.status === 'done') {
  console.log(snapshot.output);
} else if (snapshot.status === 'error') {
  console.error(snapshot.error);
}

output is only defined for done snapshots, and error only for error snapshots. TypeScript narrows both from status.

Immutability and identity

Snapshots are never mutated. Each transition produces a new snapshot object, so a previously captured snapshot keeps describing the moment it was read.

An event that takes no transition produces no new object: subscribers are still notified, with the same snapshot reference as before.

const before = actor.getSnapshot();
actor.send({ type: 'unhandled' });
actor.getSnapshot() === before; // true

This makes reference equality a valid re-render check in UI bindings. For finer-grained comparisons, use selectors.

Waiting for a snapshot

waitFor(...) resolves with the first snapshot that satisfies a predicate, including the current one.

import { waitFor } from 'xstate';

const snapshot = await waitFor(actor, (snapshot) =>
  snapshot.matches('ready')
);
OptionDefaultDescription
timeoutInfinityMilliseconds before rejecting with a timeout error.
signalnoneAn AbortSignal that stops waiting and rejects with the signal's reason.
const snapshot = await waitFor(actor, (snapshot) => snapshot.hasTag('loaded'), {
  timeout: 5_000,
  signal: request.signal
});

The promise also rejects if the actor errors, or if it terminates without ever satisfying the predicate. Use waitFor(...) in a test or a request handler that must block until the machine reaches a state.

To wait for completion instead of a specific state, use toPromise(actor), which resolves with the actor's output and rejects on error.

import { toPromise } from 'xstate';

const output = await toPromise(actor);

Child snapshots

snapshot.children holds the actor's invoked and spawned children, keyed by their id. Each child is an actor reference with its own snapshot.

const upload = actor.getSnapshot().children.upload;

upload?.getSnapshot().context.progress;

Children appear while the state that owns them is active and disappear when it is exited, so read them defensively. getPersistedSnapshot() includes child state, so a restored actor restores its children too. See persistence.

TypeScript

Use SnapshotFrom to get the snapshot type for actor logic or an actor reference.

import type { SnapshotFrom } from 'xstate';

type MachineSnapshot = SnapshotFrom<typeof machine>;

Snapshots cheatsheet

actor.getSnapshot();
actor.getSnapshot().status;
actor.getSnapshot().output;
actor.getSnapshot().error;
actor.subscribe((snapshot) => console.log(snapshot));
actor.getSnapshot().can({ type: 'submit' });
actor.getSnapshot().children.upload?.getSnapshot();

await waitFor(actor, (snapshot) => snapshot.status === 'done');
await waitFor(actor, (snapshot) => snapshot.matches('ready'), {
  timeout: 5_000
});
await toPromise(actor);

On this page