Stately
XState v6 alpha

Matching states

View a machine snapshot as a tagged union.

XState v6 is in alpha

APIs and behavior may change before the stable release.

taggedState(snapshot) views a machine snapshot as a member of a tagged union, so Match.tag and Match.exhaustive work on states.

The returned value has four fields:

FieldDescription
_tagThe state's dotted path, such as 'checkout.paying'.
valueThe snapshot's state value.
contextThat state's context, including any per-state context schema.
snapshotThe snapshot itself.

TaggedState<typeof machine> names the union for a machine. TaggedStateFrom<TSnapshot> names it for a snapshot type. StateTag<TValue> is the tag of a single state value.

import { Match, Stream } from 'effect';
import { snapshots, taggedState, type TaggedState } from '@xstate/effect';

const describe = Match.type<TaggedState<typeof checkoutMachine>>().pipe(
  Match.tag('cart', ({ context }) => `${context.items.length} items`),
  Match.tag('paying', ({ context }) => `paying ${context.paymentId}`),
  Match.tag('done.paid', 'done.declined', ({ _tag }) => _tag),
  Match.exhaustive
);

const labels = snapshots(actor).pipe(
  Stream.map(taggedState),
  Stream.map(describe)
);

Because context is the context of that state, a per-state context schema declared in setupEffect({ states }) narrows with the tag. See schemas and actions.

Parallel states

A parallel state is in several regions at once, so it has no single dotted path. Its tag stops at the parallel state, and is '(machine)' when the machine itself is parallel. Match on value or snapshot.matches for the regions of a parallel state.

const region = Match.type<TaggedState<typeof parallelMachine>>().pipe(
  Match.tag('(machine)', ({ snapshot }) =>
    snapshot.matches({ upload: 'sending' }) ? 'sending' : 'idle'
  ),
  Match.exhaustive
);

The state atom

createActorAtoms exposes the same view as its state atom, so a UI can match on states without calling taggedState itself. See atoms and React.

On this page