Stately
XState v6 alpha

States

Define state nodes and read machine snapshots.

XState v6 is in alpha

APIs and behavior may change before the stable release.

State nodes are values in a machine's states object.

const machine = createMachine({
  initial: 'idle',
  states: {
    idle: {},
    active: {},
    complete: { type: 'final' }
  }
});

State node types

  • Atomic states have no children.
  • Compound states have children and one active child at a time.
  • Parallel states activate every child region at once.
  • Final states complete their parent and can produce output.
  • History states remember the previously active child.
  • Choice states resolve to a target instead of resting.

See machine configuration for every property a state node accepts.

State snapshots

actor.getSnapshot() returns a machine snapshot: an immutable description of the machine at one point in time.

MemberTypeDescription
valuestring or nested objectThe active state value.
contextobjectCurrent context.
status'active' | 'done' | 'error' | 'stopped'Snapshot status.
childrenRecord<string, ActorRef>Invoked and spawned child actors, keyed by id.
outputoutput type or undefinedMachine output; only set when status is 'done'.
errorunknownOnly set when status is 'error'.
tagsSet<string>Tags of all active state nodes.
nodesStateNode[]Active state nodes represented by value.
historyValueobjectRemembered values for history states.
timersRecord<string, LogicalTimer>Pending delayed and timeout timers.
machineStateMachineThe machine that produced this snapshot.
MethodReturnsDescription
matches(partialValue)booleanWhether the state value matches a partial value. Narrows the snapshot type.
can(event)booleanWhether the event would take a non-forbidden transition.
hasTag(tag)booleanWhether any active state node has the tag.
getMeta()Record<stateId, meta>Metadata of active state nodes.
getInputs()Record<stateId, input>State input of active state nodes.
toJSON()objectSerializable form, with tags as an array. See serialization.

Matching state values

matches(...) accepts a partial state value. A parent match succeeds no matter which child is active.

snapshot.matches('active');
snapshot.matches({ checkout: 'payment' });
snapshot.matches({ checkout: { payment: 'authorizing' } });

For parallel states, pass only the regions to check. Unlisted regions are ignored.

// value: { player: { playback: 'playing', volume: 'muted' } }
snapshot.matches({ player: { playback: 'playing' } }); // true

matches(...) narrows the snapshot type, so context that only exists in that state becomes available inside the branch.

Checking events

can(...) answers whether sending an event right now would select a transition that is not forbidden, even if that transition has no actions and does not change the state value.

snapshot.can({ type: 'submit' });

Because v6 conditions live inside transition functions, can(...) calls those functions to see whether they return a transition. It does not execute their effects: enqueued actions, raised events and sent events are discarded. Keep transition functions free of side effects outside enq(...) so that can(...) stays safe to call during rendering.

A checkout button can be disabled with can({ type: 'submit' }) instead of duplicating the machine's rules.

Tags

Tags label states that share a meaning. Declare them per state node, and their type with schemas.tags.

const machine = createMachine({
  schemas: { tags: z.enum(['busy', 'error']) },
  initial: 'idle',
  states: {
    idle: {},
    uploading: { tags: ['busy'] },
    processing: { tags: ['busy'] }
  }
});

snapshot.hasTag('busy');
snapshot.tags; // Set { 'busy' }

uploading and processing can both render the same spinner without the view knowing either state name.

Metadata

meta attaches static data to a state node. getMeta() returns the metadata of every active state node, keyed by state node id.

const machine = createMachine({
  id: 'upload',
  schemas: { meta: z.object({ label: z.string() }) },
  initial: 'selecting',
  states: {
    selecting: { meta: { label: 'Choose a file' } },
    uploading: { meta: { label: 'Uploading…' } }
  }
});

snapshot.getMeta(); // { 'upload.uploading': { label: 'Uploading…' } }

State nodes without meta are omitted. Metadata must match schemas.meta.

TypeScript

Literal state keys are inferred from the machine configuration, so matches(...) rejects unknown values. Use SnapshotFrom<typeof machine> for the snapshot type.

import type { SnapshotFrom } from 'xstate';

type UploadSnapshot = SnapshotFrom<typeof machine>;

States cheatsheet

snapshot.value;
snapshot.context;
snapshot.status;
snapshot.children;
snapshot.output;
snapshot.error;
snapshot.matches('idle');
snapshot.matches({ loading: 'user' });
snapshot.can({ type: 'submit' });
snapshot.hasTag('busy');
snapshot.getMeta();
snapshot.getInputs();
snapshot.toJSON();

On this page