Stately
XState v6 alpha

Input and restored snapshots

Start Svelte actors with input or a persisted snapshot.

XState v6 is in alpha

APIs and behavior may change before the stable release.

input and snapshot are actor options, so they are passed as the second argument to useActor(...), useMachine(...) or useActorRef(...).

const { snapshot, send } = useActor(checkoutMachine, {
  input: { cartId },
  snapshot: restoredSnapshot
});

input creates a fresh initial state from data the actor needs when it starts. A persisted snapshot resumes an actor that already ran. Both are read once, when the component initializes.

Input from props

<script lang="ts">
  export let cartId: string;

  const { snapshot, send } = useActor(checkoutMachine, { input: { cartId } });
</script>

Changing cartId later does not restart the actor, because the option was already consumed and the reactive statement would run against an actor that is already past its initial state. Choose one of:

  • send an event, such as send({ type: 'cart.changed', cartId }), and model the change in the machine
  • give the component a {#key cartId} block so Svelte destroys the old actor and creates a new one

Machines that declare required input make the options argument mandatory in TypeScript.

Persisting a snapshot

actorRef.getPersistedSnapshot() returns a serializable value, including persisted children. Save it as the actor changes.

<script lang="ts">
  import { onDestroy } from 'svelte';
  import { useActor } from '@xstate/svelte';

  const { snapshot, send, actorRef } = useActor(uploadMachine, {
    snapshot: loadSnapshot()
  });

  const subscription = actorRef.subscribe(() => {
    localStorage.setItem(
      'upload',
      JSON.stringify(actorRef.getPersistedSnapshot())
    );
  });

  onDestroy(() => subscription.unsubscribe());
</script>

Read it back before the actor is created:

function loadSnapshot() {
  if (typeof localStorage === 'undefined') return undefined;
  const raw = localStorage.getItem('upload');
  if (!raw) return undefined;
  try {
    return JSON.parse(raw);
  } catch {
    return undefined;
  }
}

The typeof localStorage check matters in SvelteKit, where component scripts also run on the server. Stored snapshots are untrusted input: validate them and fall back to undefined when they do not fit the current machine. See Persist and restore actors for versioning.

What restoring does

A restored actor starts in the persisted state. Actions that already ran are not re-executed. Invoked actors are restarted and spawned actors are restored recursively, so a machine restored inside uploading starts the upload request again.

Warning: Do not persist a snapshot that holds values which cannot survive JSON.stringify: DOM nodes, file handles, promises. Keep those out of context, or model them as invoked actors that are recreated on restore.

Cheatsheet

useActor(logic, { input });
useActor(logic, { snapshot: persisted });
useActorRef(logic, { input, snapshot });

actorRef.getPersistedSnapshot();

On this page