Skip to main content
A Snapshot represents the state of an actor at a specific point in time. Different types of actors have different snapshot structures.

Type

interface Snapshot<TOutput> {
  /** The status of the actor snapshot */
  status: 'active' | 'done' | 'error' | 'stopped';
  /** The output data of the actor when in a final state */
  output?: TOutput;
  /** The error that caused the actor to fail */
  error?: unknown;
}

Properties

status
'active' | 'done' | 'error' | 'stopped'
required
The current status of the actor.
  • 'active' - The actor is running and can receive events
  • 'done' - The actor has completed successfully and produced output
  • 'error' - The actor encountered an error
  • 'stopped' - The actor has been stopped
output
TOutput | undefined
The output data produced by the actor when it reaches a final state.Only defined when status is 'done'.
error
unknown
The error that caused the actor to enter an error state.Only defined when status is 'error'.

Snapshot Types

Different actor types extend the base Snapshot interface with additional properties:

MachineSnapshot

For state machine actors created with createMachine():
interface MachineSnapshot<TContext, TEvent, ...> extends Snapshot<TOutput> {
  value: StateValue;
  context: TContext;
  tags: Set<string>;
  children: Record<string, ActorRef>;
  matches(stateValue: StateValue): boolean;
  hasTag(tag: string): boolean;
  can(event: TEvent): boolean;
  getMeta(): Record<string, any>;
}
See State API for full details.

PromiseSnapshot

For promise actors created with fromPromise():
type PromiseSnapshot<TOutput> = 
  | { status: 'active'; output: undefined; error: undefined }
  | { status: 'done'; output: TOutput; error: undefined }
  | { status: 'error'; output: undefined; error: unknown }

CallbackSnapshot

For callback actors created with fromCallback():
interface CallbackSnapshot {
  status: 'active';
  output: undefined;
  error: undefined;
}

ObservableSnapshot

For observable actors created with fromObservable():
type ObservableSnapshot<TOutput> =
  | { status: 'active'; output: undefined; error: undefined; context: TOutput }
  | { status: 'done'; output: undefined; error: undefined; context: TOutput }
  | { status: 'error'; output: undefined; error: unknown; context: undefined }

TransitionSnapshot

For transition actors created with fromTransition():
interface TransitionSnapshot<TContext> {
  status: 'active';
  output: undefined;
  error: undefined;
  context: TContext;
}

Helper Types

SnapshotFrom

Extracts the snapshot type from actor logic:
import { SnapshotFrom } from 'xstate';

const machine = createMachine({...});

type MySnapshot = SnapshotFrom<typeof machine>;

OutputFrom

Extracts the output type from actor logic:
import { OutputFrom } from 'xstate';

const promiseLogic = fromPromise(async () => ({ data: 'result' }));

type MyOutput = OutputFrom<typeof promiseLogic>;
// => { data: string }

Examples

Checking snapshot status

import { createActor, fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
  const response = await fetch('/api/data');
  return response.json();
});

const actor = createActor(promiseLogic);

actor.subscribe((snapshot) => {
  switch (snapshot.status) {
    case 'active':
      console.log('Loading...');
      break;
    case 'done':
      console.log('Success:', snapshot.output);
      break;
    case 'error':
      console.error('Failed:', snapshot.error);
      break;
  }
});

actor.start();

Using snapshots with machines

import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  initial: 'idle',
  context: { count: 0 },
  states: {
    idle: {
      on: { START: 'running' }
    },
    running: {
      on: { STOP: 'idle' }
    }
  }
});

const actor = createActor(machine);

actor.subscribe((snapshot) => {
  console.log('Status:', snapshot.status);
  console.log('State:', snapshot.value);
  console.log('Context:', snapshot.context);
});

actor.start();

Type-safe snapshots

import { createMachine, createActor, SnapshotFrom } from 'xstate';

const machine = createMachine({
  types: {} as {
    context: { count: number };
    output: number;
  },
  initial: 'counting',
  context: { count: 0 },
  states: {
    counting: {
      on: {
        INCREMENT: {
          actions: assign({ count: ({ context }) => context.count + 1 })
        },
        FINISH: 'done'
      }
    },
    done: {
      type: 'final'
    }
  },
  output: ({ context }) => context.count
});

type MySnapshot = SnapshotFrom<typeof machine>;

function handleSnapshot(snapshot: MySnapshot) {
  if (snapshot.status === 'done') {
    // TypeScript knows snapshot.output is a number
    console.log('Final count:', snapshot.output);
  }
  
  // TypeScript knows snapshot.context has a count property
  console.log('Current count:', snapshot.context.count);
}

const actor = createActor(machine);
actor.subscribe(handleSnapshot);
actor.start();

Persisting snapshots

import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  initial: 'active',
  context: { value: 0 },
  states: {
    active: {
      on: {
        INCREMENT: {
          actions: assign({ value: ({ context }) => context.value + 1 })
        }
      }
    }
  }
});

const actor = createActor(machine);
actor.start();

actor.send({ type: 'INCREMENT' });
actor.send({ type: 'INCREMENT' });

// Get persisted snapshot
const persistedSnapshot = actor.getPersistedSnapshot();

// Save to storage
localStorage.setItem('actorState', JSON.stringify(persistedSnapshot));

// Later, restore from storage
const savedState = JSON.parse(localStorage.getItem('actorState'));

const restoredActor = createActor(machine, {
  snapshot: savedState
});
restoredActor.start();

console.log(restoredActor.getSnapshot().context.value); // 2

Observable snapshots

import { createActor, fromObservable } from 'xstate';
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

const observableLogic = fromObservable(() => 
  interval(1000).pipe(
    map(n => ({ count: n }))
  )
);

const actor = createActor(observableLogic);

actor.subscribe((snapshot) => {
  if (snapshot.status === 'active') {
    console.log('Count:', snapshot.context.count);
  }
});

actor.start();

Notes

  • Snapshots are immutable - they represent a point-in-time state
  • The status field is always present on all snapshot types
  • Machine snapshots have additional properties like value, context, and tags
  • Use getSnapshot() to read the current snapshot synchronously
  • Use getPersistedSnapshot() to get a serializable snapshot for persistence

See also