Skip to main content

Overview

The useSelector function subscribes to an actor and returns a derived value from its snapshot as a Svelte readable store. It only triggers updates when the selected value changes.
function useSelector<TActor, T>(
  actor: TActor,
  selector: (snapshot: SnapshotFrom<TActor>) => T,
  compare?: (a: T, b: T) => boolean
): Readable<T>

Basic Usage

<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { createMachine } from 'xstate';

  const machine = createMachine({
    context: { count: 0, name: 'Alice' },
    // ...
  });

  const { actorRef } = useActor(machine);

  // Only updates when count changes
  const count = useSelector(actorRef, (state) => state.context.count);

  // Only updates when name changes
  const name = useSelector(actorRef, (state) => state.context.name);
</script>

<div>
  <p>Count: {$count}</p>
  <p>Name: {$name}</p>
</div>

Custom Comparison

By default, useSelector uses strict equality (===) to compare values. Provide a custom comparison function for complex objects:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { myMachine } from './myMachine';

  const { actorRef } = useActor(myMachine);

  function shallowEqual(a, b) {
    if (a === b) return true;
    if (!a || !b) return false;
    
    const keysA = Object.keys(a);
    const keysB = Object.keys(b);
    
    if (keysA.length !== keysB.length) return false;
    
    return keysA.every(key => a[key] === b[key]);
  }

  const user = useSelector(
    actorRef,
    (state) => state.context.user,
    shallowEqual
  );
</script>

Performance Optimization

Use useSelector to optimize rendering by only subscribing to specific parts of state:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { largeMachine } from './largeMachine';

  const { actorRef } = useActor(largeMachine);

  // Component only re-renders when username changes,
  // not when other context values change
  const username = useSelector(
    actorRef,
    (state) => state.context.user.name
  );

  // Component only re-renders when this specific state is active
  const isEditMode = useSelector(
    actorRef,
    (state) => state.matches({ editing: 'user' })
  );
</script>

<div>
  <h1>{$username}</h1>
  {#if $isEditMode}
    <form>
      <!-- Edit form -->
    </form>
  {/if}
</div>

Multiple Selectors

Create multiple selectors for different values:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { dashboardMachine } from './dashboardMachine';

  const { actorRef, send } = useActor(dashboardMachine);

  const userName = useSelector(
    actorRef,
    (state) => state.context.user.name
  );

  const notifications = useSelector(
    actorRef,
    (state) => state.context.notifications
  );

  const unreadCount = useSelector(
    actorRef,
    (state) => state.context.notifications.filter(n => !n.read).length
  );

  const currentPage = useSelector(
    actorRef,
    (state) => state.value
  );
</script>

<div>
  <header>
    <span>{$userName}</span>
    {#if $unreadCount > 0}
      <span>{$unreadCount} new notifications</span>
    {/if}
  </header>
  <main>
    <p>Current page: {$currentPage}</p>
  </main>
</div>

Reactive Statements

Combine selectors with Svelte’s reactive statements:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { myMachine } from './myMachine';

  const { actorRef } = useActor(myMachine);

  const items = useSelector(
    actorRef,
    (state) => state.context.items
  );

  // Reactive derived values
  $: sortedItems = [...$items].sort((a, b) => a.name.localeCompare(b.name));
  $: itemCount = $items.length;
  $: hasItems = itemCount > 0;
</script>

<div>
  {#if hasItems}
    <p>Total items: {itemCount}</p>
    <ul>
      {#each sortedItems as item}
        <li>{item.name}</li>
      {/each}
    </ul>
  {:else}
    <p>No items</p>
  {/if}
</div>

Selecting from Spawned Actors

Use useSelector with actors spawned from a parent machine:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { parentMachine } from './parentMachine';

  const { actorRef: parent } = useActor(parentMachine);

  // Get spawned child actor
  const childActor = useSelector(
    parent,
    (state) => state.context.childActor
  );

  // Select from child if it exists
  $: childValue = $childActor?.getSnapshot().context.value ?? null;
</script>

<div>
  {#if $childActor}
    <p>Child value: {childValue}</p>
  {/if}
</div>

Manual Store Subscription

Since useSelector returns a Svelte store, you can subscribe manually:
<script>
  import { useActor, useSelector } from '@xstate/svelte';
  import { onMount } from 'svelte';
  import { myMachine } from './myMachine';

  const { actorRef } = useActor(myMachine);
  const value = useSelector(actorRef, (state) => state.context.value);

  onMount(() => {
    // Manual subscription
    const unsubscribe = value.subscribe((val) => {
      console.log('Value changed:', val);
    });

    return unsubscribe;
  });
</script>

TypeScript

The selector function is fully typed:
import { setup } from 'xstate';
import { useActor, useSelector } from '@xstate/svelte';
import type { Readable } from 'svelte/store';

interface User {
  id: string;
  name: string;
  email: string;
}

const machine = setup({
  types: {
    context: {} as {
      user: User;
      count: number;
    }
  }
}).createMachine({
  context: {
    user: { id: '1', name: 'Alice', email: 'alice@example.com' },
    count: 0
  }
});

const { actorRef } = useActor(machine);

// TypeScript infers the return type
const userName: Readable<string> = useSelector(
  actorRef,
  (state) => state.context.user.name
);

const userEmail: Readable<string> = useSelector(
  actorRef,
  (state) => state.context.user.email
);

Implementation Details

The useSelector function:
  1. Gets the initial selected value from the actor’s current snapshot
  2. Creates a Svelte readable store with the selected value
  3. Subscribes to actor changes in the store’s start function
  4. Calls the selector on each new snapshot
  5. Compares the new selected value with the previous using the comparison function
  6. Only updates the store when the comparison returns false
  7. Automatically unsubscribes when the store has no subscribers

useActor

Create and manage actors with reactive stores

Svelte Integration

Back to Svelte integration overview