# Source: https://stately.ai/llms-full.txt

# Actions (/docs/actions)

Actions are fire-and-forget effects. When a state machine transitions, it may execute actions. Actions occur in response to events, and are typically defined on transitions in the `actions: [...]` property. Actions can also be defined for any transition that enters a state in the state's `entry: [...]` property, or for any transition that exits a state in the state's `exit: [...]` property.

<Callout>
  You can visualize your state machines and easily add actions in our drag-and-drop Stately editor. [Read more about actions in Stately’s editor](editor-actions-and-actors).
</Callout>

Actions can also be on a state’s `entry` or `exit`, also as a single action or an array.

```ts
import { setup } from 'xstate';

function trackResponse(response: string) {
  // ...
}

const feedbackMachine = setup({
  actions: {
    track: (_, params: { response: string }) => {
      trackResponse(params.response);
      // Tracks { response: 'good' }
    },
    showConfetti: () => {
      // ...
    }
  }
}).createMachine({
  // ...
  states: {
    // ...
    question: {
      on: {
        'feedback.good': {
          actions: [
            { type: 'track', params: { response: 'good' } }
          ]
        }
      },
      exit: [
        { type: 'exitAction' }
      ]
    }
    thanks: {
      entry: [
        { type: 'showConfetti' }
      ],
    }
  }
});
```

Examples of actions:

* Logging a message
* Sending a message to another [actor](actors)
* Updating context

## Entry and exit actions

Entry actions are actions that occur on any transition that enters a state node. Exit actions are actions that occur on any transition that exits a state node.

Entry and exit actions are defined using the `entry: [...]` and `exit: [...]` attributes on a state node. You can fire multiple entry and exit actions on a state. Top-level final states cannot have exit actions, since the machine is stopped and no further transitions can occur.

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=f46674a5-4da3-4aca-9900-17c6ef471f50" title="Feedback form" />

## Action objects

Action objects have an action `type` and an optional `params` object:

* The action `type` property describes the action. Actions with the same type have the same implementation.
* The action `params` property hold parameterized values that are relevant to the action.

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  actions: {
    track: (_, params: { response: string }) => {
      /* ... */
    },
  },
}).createMachine({
  // ...
  states: {
    // ...
    question: {
      on: {
        'feedback.good': {
          actions: [
            // [!code highlight:6]
            {
              // Action type
              type: 'track',
              // Action params
              params: { response: 'good' },
            },
          ],
        },
      },
    },
  },
});
```

## Dynamic action parameters

You can dynamically pass parameters in the `params` property to action objects by using a function that returns the params. The function takes in an object that contains the current `context` and `event` as arguments.

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  actions: {
    logInitialRating: (_, params: { initialRating: number }) => {
      // ...
    },
  },
}).createMachine({
  context: {
    initialRating: 3,
  },
  entry: [
    {
      type: 'logInitialRating',
      // [!code highlight:3]
      params: ({ context }) => ({
        initialRating: context.initialRating,
      }),
    },
  ],
});
```

This is a recommended approach for making actions more reusable, since you can define actions that do not rely on the machine’s `context` or `event` types.

```ts
import { setup } from 'xstate';

// [!code highlight:3]
function logInitialRating(_, params: { initialRating: number }) {
  console.log(`Initial rating: ${params.initialRating}`);
}

const feedbackMachine = setup({
  actions: { logInitialRating },
}).createMachine({
  context: { initialRating: 3 },
  entry: [
    {
      type: 'logInitialRating',
      // [!code highlight:3]
      params: ({ context }) => ({
        initialRating: context.initialRating,
      }),
    },
  ],
});
```

## Inline actions

You can declare actions as inline functions:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  entry: [
    // [!code highlight:4]
    // Inline action
    ({ context, event }) => {
      console.log(/* ... */);
    },
  ],
});
```

Inline actions are useful for prototyping and simple cases but we generally recommended using action objects.

## Implementing actions

You can setup the implementations for named actions in the `actions` property of the `setup(...)` function

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  // [!code highlight:6]
  actions: {
    track: (_, params: { msg: string }) => {
      // Action implementation
      // ...
    },
  },
}).createMachine({
  // Machine config
  entry: [{ type: 'track', params: { msg: 'entered' } }],
});
```

You can also provide action implementations to override existing actions in the `machine.provide(...)` method, which creates a new machine with the same config but with the provided implementations:

```ts
import { createActor } from 'xstate';

const feedbackActor = createActor(
  // [!code highlight:9]
  feedbackMachine.provide({
    actions: {
      track: ({ context, event }, params) => {
        // Different action implementation
        // (overrides previous implementation)
        // ...
      },
    },
  }),
);
```

## Built-in actions

XState provides a number of useful built-in actions that are a core part of the logic of your state machines, and not merely side-effects.

<Callout type="warning">
  Built-in actions, such as `assign(…)`, `sendTo(…)`, and `raise(…)`, are **not imperative**; they return a special [action object](#action-objects) (e.g. `{ type: 'xstate.assign', … }`) that are interpreted by the state machine. Do not call built-in action in custom action functions.

  ```ts
  import { createMachine, assign, enqueueActions } from 'xstate';

  // ❌ This will have no effect
  const machine = createMachine({
    context: { count: 0 },
    // [!code highlight:5]
    entry: ({ context }) => {
      // This action creator only returns an action object
      // like { type: 'xstate.assign', ... }
      assign({ count: context.count + 1 });
    },
  });

  // ✅ This will work as expected
  const machine = createMachine({
    context: { count: 0 },
    // [!code highlight:3]
    entry: assign({
      count: ({ context }) => context.count + 1,
    }),
  });

  // ✅ Imperative built-in actions are available in `enqueueActions(…)`
  const machine = createMachine({
    context: { count: 0 },
    // [!code highlight:5]
    entry: enqueueActions(({ context, enqueue }) => {
      enqueue.assign({
        count: context.count + 1,
      });
    }),
  });
  ```
</Callout>

## Assign action

The `assign(...)` action is a special action that assigns data to the state context. The `assignments` argument in `assign(assignments)` is where assignments to context are specified.

Assignments can be an object of key-value pairs where the keys are `context` keys and the values are either static values or expressions that return the new value:

```ts
import { setup } from 'xstate';

const countMachine = setup({
  types: {
    events: {} as { type: 'increment'; value: number },
  },
}).createMachine({
  context: {
    count: 0,
  },
  on: {
    increment: {
      // [!code highlight:3]
      actions: assign({
        count: ({ context, event }) => context.count + event.value,
      }),
    },
  },
});

const countActor = createActor(countMachine);
countActor.subscribe((state) => {
  console.log(state.context.count);
});
countActor.start();
// logs 0

countActor.send({ type: 'increment', value: 3 });
// logs 3

countActor.send({ type: 'increment', value: 2 });
// logs 5
```

For more dynamic assignments, the argument passed to `assign(...)` may also be a function that returns the partial or full `context` value:

```ts
import { setup } from 'xstate';

const countMachine = setup({
  types: {
    events: {} as { type: 'increment'; value: number },
  },
}).createMachine({
  context: {
    count: 0,
  },
  on: {
    increment: {
      // [!code highlight:5]
      actions: assign(({ context, event }) => {
        return {
          count: context.count + event.value,
        };
      }),
    },
  },
});
```

<Callout type="warning">
  Do not mutate the `context` object. Instead, you should use the `assign(...)` action to update `context` immutably. If you mutate the `context` object, you may get unexpected behavior, such as mutating the `context` of other actors.
</Callout>

<Callout>
  You can create state machines with the `assign(...)` action in our drag-and-drop Stately editor. [Read more about built-in assign action in Stately’s editor](/docs/editor-actions-and-actors/#xstate-built-in-actions).
</Callout>

## Raise action

The raise action is a special action that *raises* an event that is received by the same machine. Raising an event is how a machine can “send” an event to itself:

```ts
import { createMachine, raise } from 'xstate';

const machine = createMachine({
  // ...
  // [!code highlight:1]
  entry: raise({ type: 'someEvent', data: 'someData' });
});
```

Internally, when an event is raised, it is placed into an “internal event queue”. After the current transition is finished, these events are processed in insertion order ([first-in first-out, or FIFO](https://en.wikipedia.org/wiki/FIFO_\(computing_and_electronics\))). External events are only processed once all events in the internal event queue are processed.

Raised events can be dynamic:

```ts
import { createMachine, raise } from 'xstate';

const machine = createMachine({
  // ...
  // [!code highlight:4]
  entry: raise(({ context, event }) => ({
    type: 'dynamicEvent',
    data: context.someValue,
  })),
});
```

Events can also be raised with a delay, which will not place them in the internal event queue, since they will not be immediately processed:

```ts
import { createMachine, raise } from 'xstate';

const machine = createMachine({
  // ...
  entry: raise(
    { type: 'someEvent' },
    // [!code highlight:1]
    { delay: 1000 }
  );
});
```

<Callout>
  You can create state machines with the `raise(...)` action in our drag-and-drop Stately editor. [Read more about the built-in raise action in Stately’s editor](/docs/editor-actions-and-actors/#xstate-built-in-actions).
</Callout>

## Send-to action

The `sendTo(...)` action is a special action that sends an event to a specific actor.

```ts
import { createMachine, sendTo } from 'xstate';

const machine = createMachine({
  on: {
    transmit: {
      // [!code highlight:1]
      actions: sendTo('someActor', { type: 'someEvent' }),
    },
  },
});
```

The event can be dynamic:

```ts
import { createMachine, sendTo } from 'xstate';

const machine = createMachine({
  on: {
    transmit: {
      // [!code highlight:3]
      actions: sendTo('someActor', ({ context, event }) => {
        return { type: 'someEvent', data: context.someData };
      }),
    },
  },
});
```

The destination actor can be the actor ID or the actor reference itself:

```ts
import { createMachine, sendTo, fromPromise } from 'xstate';

const machine = createMachine({
  context: ({ spawn }) => ({
    someActorRef: spawn(fromPromise(/* ... */)),
  }),
  on: {
    transmit: {
      // [!code highlight:3]
      actions: sendTo(({ context }) => context.someActorRef, {
        type: 'someEvent',
      }),
    },
  },
});
```

Other options, such as `delay` and `id`, can be passed as the 3rd argument:

```ts
import { createMachine, sendTo } from 'xstate';

const machine = createMachine({
  on: {
    transmit: {
      actions: sendTo(
        'someActor',
        { type: 'someEvent' },
        // [!code highlight:4]
        {
          id: 'transmission',
          delay: 1000,
        },
      ),
    },
  },
});
```

Delayed actions can be cancelled by their `id`. See [`cancel(...)`](https://stately.ai/docs/actions#cancel-action).

<Callout>
  You can create state machines with the `sendTo(...)` action in our drag-and-drop Stately editor. [Read more about the built-in sendTo action in Stately’s editor](/docs/editor-actions-and-actors/#xstate-built-in-actions).
</Callout>

## Send-parent action

The `sendParent(...)` action is a special action that sends an event to the parent actor, if it exists.

<Callout>
  It is recommended to use `sendTo(...)` by to pass actor refs (e.g. the parent actor ref) to other actors via [input](./input.ts) or events and storing those actor refs in `context` rather than using `sendParent(...)`. This avoids tight coupling between actors and can be more type-safe.

  <details>
    <summary>
      Example using input:
    </summary>

    ```ts
    import { createMachine, sendTo } from 'xstate';

    const childMachine = createMachine({
      context: ({ input }) => ({
        parentRef: input.parentRef,
      }),
      on: {
        someEvent: {
          // [!code highlight:3]
          actions: sendTo(({ context }) => context.parentRef, {
            type: 'tellParentSomething',
          }),
        },
      },
    });

    const parentMachine = createMachine({
      // ...
      invoke: {
        id: 'child',
        src: childMachine,
        // [!code highlight:3]
        input: ({ self }) => ({
          parentRef: self,
        }),
      },
      on: {
        tellParentSomething: {
          actions: () => {
            console.log('Child actor told parent something');
          },
        },
      },
    });

    const parentActor = createActor(parentMachine);

    parentActor.start();
    ```
  </details>

  <details>
    <summary>
      Example using input (TypeScript):
    </summary>

    ```ts
    import { ActorRef, createActor, log, sendTo, setup, Snapshot } from 'xstate';

    // [!code highlight:5]
    type ChildEvent = {
      type: 'tellParentSomething';
      data?: string;
    };
    type ParentActor = ActorRef<Snapshot<unknown>, ChildEvent>;

    const childMachine = setup({
      types: {
        context: {} as {
          parentRef: ParentActor;
        },
        input: {} as {
          parentRef: ParentActor;
        },
      },
    }).createMachine({
      context: ({ input: { parentRef } }) => ({ parentRef }),
      // [!code highlight:4]
      entry: sendTo(({ context }) => context.parentRef, {
        type: 'tellParentSomething',
        data: 'Hi parent!',
      }),
    });

    export const parent = setup({
      actors: { child: childMachine },
    }).createMachine({
      // [!code highlight:6]
      invoke: {
        src: 'child',
        input: ({ self }) => ({
          parentRef: self,
        }),
      },
      on: {
        tellParentSomething: {
          actions: log(({ event: { data } }) => `Child actor says "${data}"`),
        },
      },
    });

    createActor(parent).start();
    ```
  </details>
</Callout>

## Enqueue actions

The `enqueueActions(...)` action creator is a higher-level action that enqueues actions to be executed sequentially, without actually executing any of the actions. It takes a callback that receives the `context`, `event` as well as `enqueue` and `check` functions:

* The `enqueue(...)` function is used to enqueue an action. It takes an action object or action function:

  ```ts
  actions: enqueueActions(({ enqueue }) => {
    // Enqueue an action object
    enqueue({ type: 'greet', params: { message: 'hi' } });

    // Enqueue an action function
    enqueue(() => console.log('Hello'));

    // Enqueue a simple action with no params
    enqueue('doSomething');
  });
  ```

* The `check(...)` function is used to conditionally enqueue an action. It takes a guard object or a guard function and returns a boolean that represents whether the guard evaluates to `true`:
  ```ts
  actions: enqueueActions(({ enqueue, check }) => {
    if (check({ type: 'everythingLooksGood' })) {
      enqueue('doSomething');
    }
  });
  ```

* There are also helper methods on `enqueue` for enqueueing built-in actions:
  * `enqueue.assign(...)`: Enqueues an `assign(...)` action
  * `enqueue.sendTo(...)`: Enqueues a `sendTo(...)` action
  * `enqueue.raise(...)`: Enqueues a `raise(...)` action
  * `enqueue.spawnChild(...)`: Enqueues a `spawnChild(...)` action
  * `enqueue.stopChild(...)`: Enqueues a `stopChild(...)` action
  * `enqueue.cancel(...)`: Enqueues a `cancel(...)` action

Enqueued actions can be called conditionally, but they cannot be enqueued asynchronously.

```ts
import { createMachine, enqueueActions } from 'xstate';

const machine = createMachine({
  // ...
  entry: enqueueActions(({ context, event, enqueue, check }) => {
    // assign action
    enqueue.assign({
      count: context.count + 1,
    });

    // Conditional actions (replaces choose(...))
    if (event.someOption) {
      enqueue.sendTo('someActor', { type: 'blah', thing: context.thing });

      // other actions
      enqueue('namedAction');
      // with params
      enqueue({ type: 'greet', params: { message: 'hello' } });
    } else {
      // inline
      enqueue(() => console.log('hello'));

      // even built-in actions
    }

    // Use check(...) to conditionally enqueue actions based on a guard
    if (check({ type: 'someGuard' })) {
      // ...
    }

    // no return
  }),
});
```

You can use parameters with referenced enqueue actions:

```ts
import { setup, enqueueActions } from 'xstate';

const machine = setup({
  actions: {
    // [!code highlight:4]
    doThings: enqueueActions(({ enqueue }, params: { name: string }) => {
      enqueue({ type: 'greet', params: { name } });
      // ...
    }),
    greet: (_, params: { name: string }) => {
      console.log(`Hello ${params.name}!`);
    },
  },
}).createMachine({
  // ...
  // [!code highlight:4]
  entry: {
    type: 'doThings',
    params: { name: 'World' },
  },
});
```

## Log action

The `log(...)` action is an easy way to log messages to the console.

```ts
import { createMachine, log } from 'xstate';

const machine = createMachine({
  on: {
    someEvent: {
      // [!code highlight:1]
      actions: log('some message'),
    },
  },
});
```

<Callout>
  You can create state machines with the `log(...)` action in our drag-and-drop Stately editor. [Read more about the built-in log action in Stately’s editor](/docs/editor-actions-and-actors/#xstate-built-in-actions).
</Callout>

## Cancel action

The `cancel(...)` action cancels a delayed `sendTo(...)` or `raise(...)` action by their IDs:

```ts
import { createMachine, sendTo, cancel } from 'xstate';

const machine = createMachine({
  on: {
    event: {
      actions: sendTo(
        'someActor',
        { type: 'someEvent' },
        {
          // [!code highlight:1]
          id: 'someId',
          delay: 1000,
        },
      ),
    },
    cancelEvent: {
      // [!code highlight:1]
      actions: cancel('someId'),
    },
  },
});
```

## Stop child action

The `stopChild(...)` action stops a child actor. Actors can only be stopped from their parent actor:

```ts
import { createMachine, stopChild } from 'xstate';

const machine = createMachine({
  context: ({ spawn }) => ({
    spawnedRef: spawn(fromPromise(/* ... */), { id: 'spawnedId' }),
  }),
  on: {
    stopById: {
      // [!code highlight:1]
      actions: stopChild('spawnedId'),
    },
    stopByRef: {
      // [!code highlight:1]
      actions: stopChild(({ context }) => context.spawnedRef),
    },
  },
});
```

## Modeling

If you only need to execute actions in response to events, you can create a [self-transition](/docs/transitions#self-transitions) that only has `actions: [ ... ]` defined. For example, a machine that only needs to assign to `context` in transitions may look like this:

```ts
import { createMachine } from 'xstate';

const countMachine = createMachine({
  context: {
    count: 0,
  },
  // [!code highlight:12]
  on: {
    increment: {
      actions: assign({
        count: ({ context, event }) => context.count + event.value,
      }),
    },
    decrement: {
      actions: assign({
        count: ({ context, event }) => context.count - event.value,
      }),
    },
  },
});
```

## Shorthands

For simple actions, you can specify an action string instead of an action object. Though we prefer using objects for consistency.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    // ...
    question: {
      on: {
        'feedback.good': {
          // [!code highlight:1]
          actions: ['track'],
        },
      },
    },
  },
});
```

## Actions and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

To strongly setup action types, use the `setup({ ... })` function and place the action implementations in the `actions: { ... }` object. The key is the action type and the value is the action function implementation.

You should also strongly type the parameters of the action function, which are passed as the second argument to the action function.

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:8]
  actions: {
    track: (_, params: { response: string }) => {
      // ...
    },
    increment: (_, params: { value: number }) => {
      // ...
    },
  },
}).createMachine({
  // ...
  entry: [
    { type: 'track', params: { response: 'good' } },
    { type: 'increment', params: { value: 1 } },
  ],
});
```

If you are not using `setup({ ... })` (strongly recommended), you can strongly type the `actions` of your machine in the `types.actions` property of the machine config.

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  types: {} as {
    // [!code highlight:8]
    actions:
      | {
          type: 'track';
          params: {
            response: string;
          };
        }
      | { type: 'increment'; params: { value: number } };
  },
  // ...
  entry: [
    { type: 'track', params: { response: 'good' } },
    { type: 'increment', params: { value: 1 } },
  ],
});
```

## Actions cheatsheet

### Cheatsheet: entry and exit actions

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  // [!code highlight:3]
  // Entry action on root
  entry: [{ type: 'entryAction' }],
  exit: [{ type: 'exitAction' }],
  initial: 'start',
  states: {
    start: {
      // [!code highlight:2]
      entry: [{ type: 'startEntryAction' }],
      exit: [{ type: 'startExitAction' }],
    },
  },
});
```

### Cheatsheet: transition actions

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  on: {
    someEvent: {
      actions: [
        // [!code highlight:2]
        { type: 'doSomething' },
        { type: 'doSomethingElse' },
      ],
    },
  },
});
```

### Cheatsheet: inline action functions

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  on: {
    someEvent: {
      actions: [
        // [!code highlight:3]
        ({ context, event }) => {
          console.log(context, event);
        },
      ],
    },
  },
});
```

### Cheatsheet: setting up actions

```ts
import { setup } from 'xstate';

const someAction = () => {
  //...
};

const machine = setup({
  actions: {
    someAction,
  },
}).createMachine({
  entry: [
    // [!code highlight:1]
    { type: 'someAction' },
  ],
  // ...
});
```

### Cheatsheet: providing actions

```ts
import { setup } from 'xstate';

const someAction = () => {
  //...
};

const machine = setup({
  actions: {
    someAction,
  },
}).createMachine({
  // ...
});

const modifiedMachine = machine.provide({
  someAction: () => {
    // Overridden action implementation
  },
});
```

### Cheatsheet: assign action

#### With property assigners

```ts
import { createMachine, assign } from 'xstate';

const countMachine = createMachine({
  context: {
    count: 0,
  },
  on: {
    increment: {
      // [!code highlight:5]
      actions: assign({
        count: ({ context, event }) => {
          return context.count + event.value;
        },
      }),
    },
  },
});
```

#### With function assigners

```ts
import { createMachine, assign } from 'xstate';

const countMachine = createMachine({
  context: {
    count: 0,
  },
  on: {
    increment: {
      // [!code highlight:5]
      actions: assign(({ context, event }) => {
        return {
          count: context.count + event.value,
        };
      }),
    },
  },
});
```

### Cheatsheet: raise action

```ts
import { createMachine, raise } from 'xstate';

const machine = createMachine({
  on: {
    someEvent: {
      // [!code highlight:1]
      actions: raise({ type: 'anotherEvent' }),
    },
  },
});
```

### Cheatsheet: send-to action

```ts
import { createMachine, sendTo } from 'xstate';

const machine = createMachine({
  on: {
    transmit: {
      // [!code highlight:1]
      actions: sendTo('someActor', { type: 'someEvent' }),
    },
  },
});
```

### Cheatsheet: enqueue actions

```ts
import { createMachine, enqueueActions } from 'xstate';

const machine = createMachine({
  // [!code highlight:15]
  entry: enqueueActions(({ enqueue, check }) => {
    enqueue({ type: 'someAction' });

    if (check({ type: 'someGuard' })) {
      enqueue({ type: 'anotherAction' });
    }

    enqueue.assign({
      count: 0,
    });

    enqueue.sendTo('someActor', { type: 'someEvent' });

    enqueue.raise({ type: 'anEvent' });
  }),
});
```


# The Actor model (/docs/actor-model)

The [Actor model](https://en.wikipedia.org/wiki/Actor_model) in computer science is a mathematical model of concurrent computation in which an “actor” is the basic building block.

The actor model allows developers to build reliable message-based systems by using actors to communicate. State machines and statecharts can model the logic of actors. These actors can communicate with each other, and with other actors, in the same way.

<Callout>
  When you run a state machine in XState, it becomes an actor.
</Callout>

### What defines an “actor”?

Actors are independent “live” objects that can communicate with each other via asynchronous message passing. In XState, we refer to these messages as [*events*](transitions).

* An actor has its own internal, encapsulated state that can only be updated by the actor itself. An actor may choose to update its internal state in response to a message it receives, but it cannot be updated by any other entity.
* Actors communicate with other actors by sending and receiving events asynchronously.
* Actors process one message at a time. They have an internal “mailbox” that acts like an event queue, processing events sequentially.
* Internal actor state is not shared between actors. The only way for an actor to share any part of its internal state is by:
  * Sending events to other actors
  * Or emitting snapshots, which can be considered implicit events sent to subscribers.
* Actors can create (spawn/invoke) new actors.

You’ll find strong similarities to the actor model in software you may already be familiar with. The concept of objects encapsulating state and passing messages to each other may be familiar from Object-Oriented Programming. And actors are analagous to real-world physical concepts like cell biology, and communication in human relationships.

## State

An actor has its own internal, encapsulated state that only the actor itself can update. An actor may update its internal state in response to a message it receives, but it cannot be updated by any other entity. Actors do not share state. The only way for an actor to share data is by sending events.

[Read more about XState actors and state](actors).

## Communication with events

Actors communicate with other actors by sending and receiving events asynchronously. Actors use an internal “mailbox” that acts like an event queue, processing events one at a time.

[Read more about XState events and transitions](transitions).

## Spawning

Actors can spawn new actors, which is useful in situations where an actor needs to delegate work to another actor. Spawning allows for a flexible and dynamic system where actors can be created and destroyed as needed to handle the workload efficiently.

* [Read more about spawning actors in XState](spawn).
* [Read about the difference between invoking and spawning actors in XState](actors.mdx#invoking-and-spawning-actors).

## The actor model in backend development

The actor model is often used to coordinate backend systems. There are direct implementations of the Actor model, like [Akka](https://doc.akka.io/docs/akka/current/typed/guide/introduction.html) for the JVM. In [Erlang](https://www.erlang.org/docs), processes can be seen as actors, which can send and receive messages and spawn new processes. Erlang is used by massive distributed systems, like Discord and WhatsApp.

In [Stately Sky](https://stately.ai/docs/stately-sky-getting-started), a state machine actor can be used to manage long-running backend processes like medical patient onboarding flows, inventory management, or multi-player collaborative experiences like whiteboard canvases or games.

## The actor model in frontend development

The actor model is especially useful for coordinating the many moving parts of a front-end web application.

**Your front-end app is always a distributed system**, and managing distributed systems is where the actor model shines. This is because in a browser environment **you never really have a “global source of truth”**, you instead have **many independent sources of state and events**: 3rd-party components, local component state, local storage, query parameters, routers, network I/O, DOM events and their listeners, etc.

> \[…] there is no such thing as a single source of truth in any non-trivial application. All applications, even front-end apps, are distributed at some level. – via: [Redux is Half of a Pattern (2/2)](https://dev.to/davidkpiano/redux-is-half-of-a-pattern-2-2-4jo3)

So even for simple web apps, with small app-specific state and a few known app-specific events, the actor model can be helpful.

## XState

Actors in XState can:

* **Accept messages** as [events](/docs/transitions/#event-objects) passed to their own internal logic, or for state machines as received by [transitions](transitions).
* **Create more actors** within a state machine using `spawn` in an [`assign`](/docs/actions/#assign-action), or using the `spawnChild` action creator. For details, see [Spawn](spawn).
* **Send more messages** as events using `self.send` in their own logic, or [action creators](actions) like [`sendTo`](/docs/actions/#send-to-action) or [`raise`](/docs/actions/#raise-action) in a state machine.

Actors in XState have their own [actor logic](/docs/actors/#actor-logic) which they use to:

* **Make local decisions**
* **Determine how to respond to the next message received**
* **Modify their own private state** (but only affect each other via messaging)

Actors in XState exist in [systems](system) and can communicate with each other within and across those systems.

## Reference

* [What is the actor model and when should I use it?](https://stately.ai/blog/what-is-the-actor-model)
* [The Actor Model Explained in 5 Minutes](https://www.youtube.com/watch?v=ELwEdb_pD0k)
* [Wikipedia: Actor model](https://en.wikipedia.org/wiki/Actor_model)


# Actors (/docs/actors)

import { VideoIcon } from 'lucide-react';

When you run a state machine, it becomes an actor: a running process that can receive events, send events and change its behavior based on the events it receives, which can cause effects outside of the actor.

In state machines, actors can be **invoked** or **spawned**. These are essentially the same, with the only difference being how the actor’s lifecycle is controlled.

* An **invoked actor** is started when its parent machine enters the [state](states) it is invoked in, and stopped when that state is exited.
* A **spawned actor** is started in a [transition](transitions) and stopped either with a [`stop(...)` action](/docs/actions/#stop-action) or when its parent machine is stopped.

<Callout>
  You can visualize your state machines and easily invoke actors in our drag-and-drop Stately editor.

  [Read more about actors in Stately’s editor](editor-actions-and-actors).
</Callout>

<Callout icon={<VideoIcon size={18} />}>
  Watch our [“XState: exploring actors” deep dive video on YouTube](https://www.youtube.com/watch?v=Rj7lOvDwcYs).
</Callout>

<YouTube id="Rj7lOvDwcYs" />

## Actor model

In the actor model, actors are objects that can communicate with each other. They are independent “live” entities that communicate via asynchronous message passing. In XState, these messages are referred to as *[events](transitions)*.

* An actor has its own internal, encapsulated state that can only be updated by the actor itself. An actor may choose to update its internal state in response to a message it receives, but it cannot be updated by any other entity.
* Actors communicate with other actors by sending and receiving events asynchronously.
* Actors process one message at a time. They have an internal “mailbox” that acts like an event queue, processing events sequentially.
* Internal actor state is not shared between actors. The only way for an actor to share any part of its internal state is by:
  * Sending events to other actors
  * Or emitting snapshots, which can be considered implicit events sent to subscribers.
* Actors can create (spawn/invoke) new actors.

[Read more about the Actor model](actor-model)

## Actor logic

Actor logic is the actor’s logical “model” (brain, blueprint, DNA, etc.) It describes how the actor should change behavior when receiving an event. You can create actor logic using **[actor logic creators](#actor-logic-creators)**.

In XState, actor logic is defined by an object implementing the `ActorLogic` interface, containing methods like `.transition(...)`, `.getInitialSnapshot()`, `.getPersistedSnapshot()`, and more. This object tells an interpreter how to update an actor’s internal state when it receives an event and which effects to execute (if any).

## Creating actors

You can create an actor, which is a “live” instance of some actor logic, via `createActor(actorLogic, options?)`. The `createActor(...)` function takes the following arguments:

* `actorLogic`: the [actor logic](actors.mdx#actor-logic) to create an actor from
* `options` (optional): actor options

When you create an actor from actor logic via `createActor(actorLogic)`, you implicitly create an [actor system](system) where the created actor is the root actor. Any actors spawned from this root actor and its descendants are part of that actor system. The actor must be started by calling `actor.start()`, which will also start the actor system:

```ts
import { createActor } from 'xstate';
import { someActorLogic } from './someActorLogic.ts';

const actor = createActor(someActorLogic);

actor.subscribe((snapshot) => {
  console.log(snapshot);
});

actor.start();

// Now the actor can receive events
actor.send({ type: 'someEvent' });
```

You can stop root actors by calling `actor.stop()`, which will also stop the actor system and all actors in that system:

```ts
// Stops the root actor, actor system, and actors in the system
actor.stop();
```

### Invoking and spawning actors

An invoked actor represents a state-based actor, so it is stopped when the invoking state is exited. Invoked actors are used for a finite/known number of actors.

A spawned actor represents multiple entities that can be started at any time and stopped at any time. Spawned actors are action-based and used for a dynamic or unknown number of actors.

An example of the difference between invoking and spawning actors could occur in a todo app. When loading todos, a `loadTodos` actor would be an invoked actor; it represents a single state-based task. In comparison, each of the todos can themselves be spawned actors, and there can be a dynamic number of these actors.

* [Read more about invoking actors](invoke)
* [Read more about spawning actors](spawn)

## Actor snapshots

When an actor receives an event, its internal state may change. An actor may emit a **snapshot** when a state transition occurs. You can read an actor’s snapshot synchronously via `actor.getSnapshot()`, or you can subscribe to snapshots via `actor.subscribe(observer)`.

```ts
import { fromPromise, createActor } from 'xstate';

async function fetchCount() {
  return Promise.resolve(42);
}

const countLogic = fromPromise(async () => {
  const count = await fetchCount();

  return count;
});

const countActor = createActor(countLogic);

countActor.start();

countActor.getSnapshot(); // logs undefined

// After the promise resolves...
countActor.getSnapshot();
// => {
//   output: 42,
//   status: 'done',
//   ...
// }
```

## Subscriptions

You can subscribe to an actor’s snapshot values via `actor.subscribe(observer)`. The observer will receive the actor’s snapshot value when it is emitted. The observer can be:

* A plain function that receives the latest snapshot, or
* An observer object whose `.next(snapshot)` method receives the latest snapshot

```ts
// Observer as a plain function
const subscription = actor.subscribe((snapshot) => {
  console.log(snapshot);
});
```

```ts
// Observer as an object
const subscription = actor.subscribe({
  next(snapshot) {
    console.log(snapshot);
  },
  error(err) {
    // ...
  },
  complete() {
    // ...
  },
});
```

The return value of `actor.subscribe(observer)` is a subscription object that has an `.unsubscribe()` method. You can call `subscription.unsubscribe()` to unsubscribe the observer:

```ts
const subscription = actor.subscribe((snapshot) => {
  /* ... */
});

// Unsubscribe the observer
subscription.unsubscribe();
```

When the actor is stopped, all of its observers will automatically be unsubscribed.

You can initialize actor logic at a specific persisted snapshot (state) by passing the state in the second `options` argument of `createActor(logic, options)`. If the state is compatible with the actor logic, this will create an actor that will be started at that persisted state:

```ts
const persistedState = JSON.parse(localStorage.getItem('some-persisted-state'));

const actor = createActor(someLogic, {
  // [!code highlight:1]
  snapshot: persistedState,
});

actor.subscribe(() => {
  localStorage.setItem(
    'some-persisted-state',
    JSON.stringify(actor.getPersistedSnapshot()),
  );
});

// Actor will start at persisted state
actor.start();
```

See [persistence](persistence) for more details.

## `waitFor`

You can wait for an actor’s snapshot to satisfy a predicate using the `waitFor(actor, predicate, options?)` helper function. The `waitFor(...)` function returns a promise that is:

* Resolved when the emitted snapshot satisfies the `predicate` function
* Resolved immediately if the current snapshot already satisfies the `predicate` function
* Rejected if an error is thrown or the `options.timeout` value is elapsed.

```ts
import { waitFor } from 'xstate';
import { countActor } from './countActor.ts';

const snapshot = await waitFor(
  countActor,
  (snapshot) => {
    return snapshot.context.count >= 100;
  },
  {
    timeout: 10_000, // 10 seconds (10,000 milliseconds)
  },
);

console.log(snapshot.output);
// => 100
```

## Error handling

You can subscribe to errors thrown by an actor using the `error` callback in the observer object passed to `actor.subscribe()`. This allows you to handle errors emitted by the actor logic.

```ts
import { createActor } from 'xstate';
import { someMachine } from './someMachine';

const actor = createActor(someMachine);

actor.subscribe({
  next: (snapshot) => {
    // ...
  },
  // [!code highlight:4]
  error: (err) => {
    // Handle the error here
    console.error(err);
  },
});

actor.start();
```

## Actor logic creators

The types of actor logic you can create from XState are:

* [State machine logic (`createMachine(...)`)](#createmachine)
* [Promise logic (`fromPromise(...)`)](#frompromise)
* [Transition function logic (`fromTransition(...)`)](#fromtransition)
* [Observable logic (`fromObservable(...)`)](#fromobservable)
* [Event observable logic (`fromEventObservable(...)`)](#fromeventobservable)
* [Callback logic (`fromCallback(...)`)](#fromcallback)

### Actor logic capabilities

|                                              | Receive events | Send events | Spawn actors | Input | Output |
| -------------------------------------------- | -------------- | ----------- | ------------ | ----- | ------ |
| [State machine actors](state-machine-actors) | ✅              | ✅           | ✅            | ✅     | ✅      |
| [Promise actors](promise-actors)             | ❌              | ✅           | ❌            | ✅     | ✅      |
| [Transition actors](transition-actors)       | ✅              | ✅           | ❌            | ✅     | ❌      |
| [Observable actors](observable-actors)       | ❌              | ✅           | ❌            | ✅     | ❌      |
| [Callback actors](callback-actors)           | ✅              | ✅           | ❌            | ✅     | ❌      |

### State machine logic (`createMachine(...)`)

You can describe actor logic as a [state machine](machines). Actors created from state machine actor logic can:

* Receive events
* Send events to other actors
* Invoke/spawn child actors
* Emit snapshots of its state
* Output a value when the machine reaches its top-level final state

```ts
const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {},
    active: {},
  },
});

const toggleActor = createActor(toggleMachine);

toggleActor.subscribe((snapshot) => {
  // snapshot is the machine's state
  console.log('state', snapshot.value);
  console.log('context', snapshot.context);
});
toggleActor.start();
// Logs 'inactive'
toggleActor.send({ type: 'toggle' });
// Logs 'active'
```

Learn more about [state machine actors.](state-machine-actors)

### Promise logic (`fromPromise(...)`)

Promise actor logic is described by an async process that resolves or rejects after some time. Actors created from promise logic (“promise actors”) can:

* Emit the resolved value of the promise
* Output the resolved value of the promise

Sending events to promise actors will have no effect.

```ts
const promiseLogic = fromPromise(() => {
  return fetch('https://example.com/...').then((data) => data.json());
});

const promiseActor = createActor(promiseLogic);
promiseActor.subscribe((snapshot) => {
  console.log(snapshot);
});
promiseActor.start();
// => {
//   output: undefined,
//   status: 'active'
//   ...
// }

// After promise resolves
// => {
//   output: { ... },
//   status: 'done',
//   ...
// }
```

Learn more about [promise actors.](promise-actors)

### Transition function logic (`fromTransition(...)`)

Transition actor logic is described by a [transition function](migration.mdx#use-actor-logic-creators-for-invokesrc-instead-of-functions), similar to a [reducer](cheatsheet#creating-transition-logic). Transition functions take the current `state` and received `event` object as arguments, and return the next state. Actors created from transition logic (“transition actors”) can:

* Receive events
* Emit snapshots of its state

```ts
const transitionLogic = fromTransition(
  (state, event) => {
    if (event.type === 'increment') {
      return {
        ...state,
        count: state.count + 1,
      };
    }
    return state;
  },
  { count: 0 },
);

const transitionActor = createActor(transitionLogic);
transitionActor.subscribe((snapshot) => {
  console.log(snapshot);
});
transitionActor.start();
// => {
//   status: 'active',
//   context: { count: 0 },
//   ...
// }

transitionActor.send({ type: 'increment' });
// => {
//   status: 'active',
//   context: { count: 1 },
//   ...
// }
```

Learn more about [transition actors.](transition-actors)

### Observable logic (`fromObservable(...)`)

Observable actor logic is described by an [observable stream of values](#fromObservable). Actors created from observable logic (“observable actors”) can:

* Emit snapshots of the observable’s emitted value

Sending events to observable actors will have no effect.

```ts
import { interval } from 'rxjs';

const secondLogic = fromObservable(() => interval(1000));

const secondActor = createActor(secondLogic);

secondActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});

secondActor.start();
// At every second:
// Logs 0
// Logs 1
// Logs 2
// ...
```

Learn more about [observable actors.](observable-actors)

### Event observable logic (`fromEventObservable(...)`)

Event observable actor logic is described by an observable stream of [event objects](transitions.mdx#event-objects). Actors created from event observable logic (“event observable actors”) can:

* Implicitly send events to its parent actor
* Emit snapshots of its emitted event objects

Sending events to event observable actors will have no effect.

```ts
import { setup, fromEventObservable } from 'xstate';
import { fromEvent } from 'rxjs';

const mouseClickLogic = fromEventObservable(
  () => fromEvent(document.body, 'click') as Subscribable<EventObject>,
);

const canvasMachine = setup({
  actors: {
    mouseClickLogic,
  },
}).createMachine({
  invoke: {
    // Will send mouse click events to the canvas actor
    src: 'mouseClickLogic',
  },
});

const canvasActor = createActor(canvasMachine);
canvasActor.start();
```

Learn more about [observable actors.](observable-actors)

### Callback logic (`fromCallback(...)`)

Callback actor logic is described by a callback function that receives a single object argument that includes a `sendBack(event)` function and a `receive(event => ...)` function. Actors created from callback logic (“callback actors”) can:

* Receive events via the `receive` function
* Send events to the parent actor via the `sendBack` function

```ts
const callbackLogic = fromCallback(({ sendBack, receive }) => {
  let lockStatus = 'unlocked';

  const handler = (event) => {
    if (lockStatus === 'locked') {
      return;
    }
    sendBack(event);
  };

  receive((event) => {
    if (event.type === 'lock') {
      lockStatus = 'locked';
    } else if (event.type === 'unlock') {
      lockStatus = 'unlocked';
    }
  });

  document.body.addEventListener('click', handler);

  return () => {
    document.body.removeEventListener('click', handler);
  };
});
```

Callback actors are a bit different from other actors in that they do not do the following:

* Do not work with `onDone`
* Do not produce a snapshot using `.getSnapshot()`
* Do not emit values when used with `.subscribe()`

You may choose to use `sendBack` to report caught errors to the parent actor. This is especially helpful for handling promise rejections within a callback function, which will not be caught by [`onError`](invoke.mdx#onerror).

Callback functions cannot be `async` functions. But it is possible to execute a Promise within a callback function.

```ts
import { setup, fromCallback } from 'xstate';

const someCallback = fromCallback(({ sendBack }) => {
  // [!code highlight:3]
  somePromise()
    .then((data) => sendBack({ type: 'done', data }))
    .catch((error) => sendBack({ type: 'error', data: error }));

  return () => {
    /* cleanup function */
  };
});

const machine = setup({
  actors: {
    someCallback,
  },
}).createMachine({
  initial: 'running',
  states: {
    running: {
      invoke: {
        src: 'someCallback',
      },
      // [!code highlight:4]
      on: {
        error: {
          actions: ({ event }) => console.error(event.data),
        },
      },
    },
  },
});
```

Learn more about [callback actors.](callback-actors)

## Actors as promises

You can create a promise from any actor by using the `toPromise(actor)` function. The promise will resolve with the actor snapshot's `.output` when the actor is done (`snapshot.status === 'done'`) or reject with the actor snapshot's `.error` when the actor is errored (`snapshot.status === 'error'`).

```ts
import { createMachine, createActor, toPromise } from 'xstate';

const machine = createMachine({
  // ...
  states: {
    // ...
    done: { type: 'final' },
  },
  output: {
    count: 42,
  },
});

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

// [!code highlight:3]
// Creates a promise that resolves with the actor's output
// or rejects with the actor's error
const output = await toPromise(actor);

console.log(output);
// => { count: 42 }
```

If the actor is already done, the promise will resolve with the actor's `snapshot.output` immediately. If the actor is already errored, the promise will reject with the actor's `snapshot.error` immediately.

## Higher-level actor logic

Higher-level actor logic enhances existing actor logic with additional functionality. For example, you can create actor logic that logs or persists actor state:

```ts
import { fromTransition, type AnyActorLogic } from 'xstate';

const toggleLogic = fromTransition((state, event) => {
  if (event.type === 'toggle') {
    return state === 'paused' ? 'playing' : 'paused';
  }

  return state;
}, 'paused');

// [!code highlight:13]
function withLogging<T extends AnyActorLogic>(actorLogic: T) {
  const enhancedLogic = {
    ...actorLogic,
    transition: (state, event, actorCtx) => {
      console.log('State:', state);
      return actorLogic.transition(state, event, actorCtx);
    },
  } satisfies T;

  return enhancedLogic;
}

const loggingToggleLogic = withLogging(toggleLogic);
```

## Custom actor logic

Custom actor logic can be defined with an object that implements the `ActorLogic` interface.

For example, here is a custom actor logic object with a `transition` function that operates as a simple reducer:

```ts
import { createActor, EventObject, ActorLogic, Snapshot } from 'xstate';

const countLogic: ActorLogic<
  Snapshot<undefined> & { context: number },
  EventObject
> = {
  transition: (state, event) => {
    if (event.type === 'INC') {
      return {
        ...state,
        context: state.context + 1,
      };
    } else if (event.type === 'DEC') {
      return {
        ...state,
        context: state.context - 1,
      };
    }
    return state;
  },
  getInitialSnapshot: () => ({
    status: 'active',
    output: undefined,
    error: undefined,
    context: 0,
  }),
  getPersistedSnapshot: (s) => s,
};

const actor = createActor(countLogic);
actor.subscribe((state) => {
  console.log(state.context);
});
actor.start(); // => 0
actor.send({ type: 'INC' }); // => 1
actor.send({ type: 'INC' }); // => 2
```

For further examples, see implementations of `ActorLogic` in the source code, like the `fromTransition` actor logic creator, or the examples in the tests.

## Empty actors

Actor that does nothing and only has a single emitted snapshot: `undefined`

In XState, an empty actor is an actor that does nothing and only has a single emitted snapshot: `undefined`.

This is useful for testing, such as stubbing out an actor that is not yet implemented. It can also be useful in framework integrations, such as `@xstate/react`, where an actor may not be available yet:

```ts
import { createEmptyActor, AnyActorRef } from 'xstate';
import { useSelector } from '@xstate/react';
const emptyActor = createEmptyActor();

function Component(props: { actor?: AnyActorRef }) {
  const data = useSelector(
    props.actor ?? emptyActor,
    (snapshot) => snapshot.context.data,
  );

  // data is `undefined` if `props.actor` is undefined
  // Otherwise, it is the data from the actor

  // ...
}
```

## Actors and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `actors` of your machine in the `types.actors` property of the machine config.

```ts
const fetcher = fromPromise(
  async ({ input }: { input: { userId: string } }) => {
    const user = await fetchUser(input.userId);

    return user;
  },
);

const machine = setup({
  types: {
    children: {} as {
      fetch1: 'fetcher';
      fetch2: 'fetcher';
    }
  }
  // [!code highlight:1]
  actors: { fetcher }
}).createMachine({
  invoke: {
    // [!code highlight:8]
    src: 'fetchData', // strongly typed
    id: 'fetch2', // strongly typed
    onDone: {
      actions: ({ event }) => {
        event.output; // strongly typed as { result: string }
      },
    },
    input: { userId: '42' }, // strongly typed
  },
});
```

## Testing

The general strategy for testing actors is to send events and assert that the actor reaches an expected state, which can be observed either by:

* Subscribing to its emitted snapshots via `actor.subscribe(...)`
* Or reading the latest snapshot via `actor.getSnapshot()`.

```ts
test('some actor', async () => {
  const actor = createActor(
    fromTransition(
      (state, event) => {
        if (event.type === 'inc') {
          return { count: state.count + 1 };
        }
        return state;
      },
      { count: 0 },
    ),
  );

  // Start the actor
  actor.start();

  // Send event(s)
  actor.send({ type: 'inc' });
  actor.send({ type: 'inc' });
  actor.send({ type: 'inc' });

  // Assert the expected result
  expect(actor.getSnapshot().context).toEqual({ count: 3 });
});
```

## Actors cheatsheet

### Cheatsheet: create an actor

```ts
import { createActor } from 'xstate';
import { someActorLogic } from './someActorLogic.ts';

// Create an actor from the actor logic
const actor = createActor(someActorLogic);

// Subscribe to an actor’s snapshot values and log them
actor.subscribe((snapshot) => {
  console.log(snapshot);
});

// Start the actor system
actor.start();

// Now the actor can receive events
actor.send({ type: 'someEvent' });

// Stops the root actor, actor system, and actors in the system
actor.stop();
```

### Cheatsheet: state machine logic

```ts
import { createMachine, createActor } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {},
    active: {},
  },
});

const toggleActor = createActor(toggleMachine);

toggleActor.subscribe((snapshot) => {
  // snapshot is the machine’s state
  console.log('state', snapshot.value);
  console.log('context', snapshot.context);
});
toggleActor.start();
// Logs 'inactive'
toggleActor.send({ type: 'toggle' });
// Logs 'active'
```

### Cheatsheet: promise logic

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(() => {
  return fetch('https://example.com/...').then((data) => data.json());
});

const promiseActor = createActor(promiseLogic);
promiseActor.subscribe((snapshot) => {
  console.log(snapshot);
});
promiseActor.start();
```

### Cheatsheet: transition function logic

```ts
import { fromTransition, createActor } from 'xstate';

const transitionLogic = fromTransition(
  (state, event) => {
    if (event.type === 'increment') {
      return {
        ...state,
        count: state.count + 1,
      };
    }
    return state;
  },
  { count: 0 },
);

const transitionActor = createActor(transitionLogic);
transitionActor.subscribe((snapshot) => {
  console.log(snapshot);
});
transitionActor.start();
// => {
//   status: 'active',
//   context: { count: 0 },
//   ...
// }

transitionActor.send({ type: 'increment' });
// => {
//   status: 'active',
//   context: { count: 1 },
//   ...
// }
```

### Cheatsheet: observable logic

```ts
import { fromObservable, createActor } from 'xstate';
import { interval } from 'rxjs';

const secondLogic = fromObservable(() => interval(1000));

const secondActor = createActor(secondLogic);

secondActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});

secondActor.start();
// At every second:
// Logs 0
// Logs 1
// Logs 2
// ...
```

### Cheatsheet: event observable logic

```ts
import { setup, fromEventObservable, createActor } from 'xstate';
import { fromEvent } from 'rxjs';

const mouseClickLogic = fromEventObservable(
  () => fromEvent(document.body, 'click') as Subscribable<EventObject>,
);

const canvasMachine = setup({
  actors: {
    mouseClickLogic,
  },
}).createMachine({
  invoke: {
    // Will send mouse click events to the canvas actor
    src: 'mouseClickLogic',
  },
});

const canvasActor = createActor(canvasMachine);
canvasActor.start();
```

### Cheatsheet: callback logic

```ts
import { fromCallback, createActor } from 'xstate';

const callbackLogic = fromCallback(({ sendBack, receive }) => {
  let lockStatus = 'unlocked';

  const handler = (event) => {
    if (lockStatus === 'locked') {
      return;
    }
    sendBack(event);
  };

  receive((event) => {
    if (event.type === 'lock') {
      lockStatus = 'locked';
    } else if (event.type === 'unlock') {
      lockStatus = 'unlocked';
    }
  });

  document.body.addEventListener('click', handler);

  return () => {
    document.body.removeEventListener('click', handler);
  };
});
```


# Notes (/docs/annotations)

import { PlusSquare } from 'lucide-react';

You can use notes to annotate your machine, positioned anywhere inside your machine. Notes are useful for information or comments you want to add to your machine that are only visible inside Stately Studio and not included when you export as code.

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/5b170468-d66a-4136-84c8-676c8fea82e8?machineId=7879be19-d267-4e75-9a7d-6a870d540257" name="Annotated dog walk machine" />

Notes are formatted with Markdown so you can add links, images, and other formatting to your annotations.

<Callout>
  Unlike [**descriptions**](descriptions), notes don’t need to be connected to a state or transition and can be added or positioned anywhere inside your machine.
</Callout>

## Add notes to your machine

You can add notes anywhere on your machine from **Design** and **Simulate** mode, but you can only edit them in **Design** mode.

Notes are unrelated to a state or transition. Still, their layout position is connected to the closest root or parent state when you create them, which helps give your notes enough space in the auto layout.

* Use the <PlusSquare size={18} /> plus icon button in the canvas tools and choose **Note**.
* Right-click anywhere on the canvas and choose **Add note**.

### Edit and delete notes

* Double-click or use the <kbd>Enter</kbd> key to edit the contents of a note.
* Use the <kbd>Backspace</kbd> key to delete the selected note.


# Assets in Stately’s editor (/docs/assets)

import { Plus, Paperclip, Star, Info } from 'lucide-react';

# Assets

You can drag and drop assets on any state or upload them using the <Plus size={18} /> plus menu and <Paperclip size={18} /> **Asset** on any selected state.

<Callout>
  Assets are a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

Assets are valuable when you want to include use cases, features, workflows, and more to your state machines. One of our most frequent requests is from teams who want to tie their logic to real-life user interfaces, and assets provide a way to connect user interface design to logic in a way your whole team can understand.

<Callout>
  You can now [embed Figma frames](figma) that stay in sync with your Figma files.
</Callout>

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=bf81f493-6855-43e7-a6aa-4551f1acdc6e" title="State assets" />

## Order of assets

The first asset, indicated by the <Star size={18} /> star icon, is the asset shown on the canvas.

Further assets are accessible from the state <Info size={18} /> **Details** panel, where you can also drag to reorder the assets and choose their [displayed size](#asset-sizes).

## Asset sizes

Once your asset is uploaded to your state, you can choose the display size from the **Size** menu that shows on hover over the asset, or from the dropdown menu in the state <Info size={18} /> **Details** panel:

* small **sm**
* medium **md**
* large **lg**
* extra large **xl**

<Callout>
  You can also add images from any URL using markdown in [state and transition descriptions](descriptions).
</Callout>


# Autolayout (/docs/autolayout)

import { Wand } from 'lucide-react';

Autolayout chooses an optimal layout for your machine to make it easier to read and understand. When you import a machine, autolayout will be applied automatically. Otherwise, you can autolayout your machine anytime using the <Wand size={18} /> **Autolayout** button.

## Autolayout from the editor menu

1. Open the editor menu from the Stately icon in the top left of Stately Studio.
2. Choose <Wand size={18} /> **Autolayout** to lay out your machine.

## Autolayout from the context menu

1. Right-click anywhere on the canvas to open the context menu.
2. Choose <Wand size={18} /> **Autolayout** to lay out your machine.

## Autolayout from the zoom menu

1. Select the current zoom level from the bottom right of Stately Studio to open the zoom menu.
2. Choose <Wand size={18} /> **Autolayout** to lay out your machine.


# Callback Actors (/docs/callback-actors)

Callback actors are actors whose logic is represented by a function that can "callback" to the parent actor by sending events (via `sendBack(...)`). It can also `receive(...)` events from other actors.

## Callback actor capabilities

|   | Capability     | Notes                                                                                                                                               |
| - | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅ | Receive events | Callback actors can receive events via the `receive(event => {...})` function.                                                                      |
| ✅ | Send events    | Callback actors can send events to its parent via `sendBack(event)`, or to other actors it has reference to, such as those provided in its `input`. |
| ❌ | Spawn actors   | Callback actors currently cannot spawn new actors.                                                                                                  |
| ✅ | Input          | You can provide `input` to callback actors.                                                                                                         |
| ❌ | Output         | Callback actors currently do not produce output – they are active indefinitely until they are stopped or an error occurs.                           |

## Callback actor logic

You can define callback actor logic using the `fromCallback(...)` actor logic creator, which takes a callback function and returns actor logic that can be used to create callback actors.

```ts
import {
  createActor,
  createMachine,
  fromCallback,
  sendTo,
  setup,
} from 'xstate';

const resizeLogic = fromCallback(({ sendBack, receive }) => {
  const resizeHandler = (event) => {
    sendBack(event);
  };

  window.addEventListener('resize', resizeHandler);

  const removeListener = () => {
    window.removeEventListener('resize', resizeHandler);
  };

  receive((event) => {
    if (event.type === 'stopListening') {
      console.log('Stopping listening');
      removeListener();
    }
  });

  // Cleanup function
  return () => {
    console.log('Cleaning up');
    removeListener();
  };
});

const machine = setup({
  actors: {
    resizeLogic,
  },
}).createMachine({
  invoke: {
    id: 'resize',
    src: 'resizeLogic',
  },
  on: {
    stop: {
      actions: sendTo('resize', { type: 'stopListening' }),
    },
  },
});

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

actor.send({ type: 'stop' });
// logs "Stopping listening" from callback actor

actor.stop();
// logs "Cleaning up" from callback actor
```

## Callback actor input

You can pass `input` when creating callback actors, which is passed to the callback actor logic in the `input` property of the first argument.

```ts
import { fromCallback, createActor, setup, type EventObject } from 'xstate';

const resizeLogic = fromCallback<EventObject, { defaultSize: number }>(
  ({
    sendBack,
    receive,
    input, // Typed as { defaultSize: number }
  }) => {
    input.defaultSize; // 100
    // ...
  },
);

const machine = setup({
  actors: {
    resizeLogic,
  },
}).createMachine({
  // ...
  invoke: {
    src: 'resizeLogic',
    input: {
      defaultSize: 100,
    },
  },
});
```


# Canvas controls (/docs/canvas-view-controls)







import { Sparkles, MousePointer2, Hand, PlusSquare, Undo, Redo, Scan, Check, ListTree } from 'lucide-react';

We’ve recently added a canvas tools panel to help you with common tasks while designing and simulating state machines.

* <Sparkles size={18} /> [Generate with AI](generate-flow).
* <MousePointer2 size={18} /> Pointer mode, for dragging and selecting items on the
  canvas.
* <Hand size={18} /> Hand mode, for panning around the canvas.
* <PlusSquare size={18} /> Quick add menu, for adding a [state](editor-states-and-transitions)
  or a [note](annotations).

{/* <p style={{ maxWidth: '300px' }}>
  <ThemedImage
    alt="Canvas tools in Stately Studio, including a sparkles AI icon, pointer icon, hand icon, and plus icon which is focused and has an open menu for adding a state or a note."
    sources={{
      light: './assets/canvas-view-controls/canvas-tools.png',
      dark: './assets/canvas-view-controls/canvas-tools-dm.png',
    }}
  />
  </p> */}

<img alt="Canvas tools in Stately Studio, including a sparkles AI icon, pointer icon, hand icon, and plus icon which is focused and has an open menu for adding a state or a note." src={__img0} placeholder="blur" />

## View controls

You spend a lot of time on the canvas while designing and simulating state machines, so we’ve added view controls to help you navigate around your machines.

## Show/hide UI (user interface)

You can show and hide most of the editor’s user interface to help you focus on your machine.

1. Open the editor menu from the Stately icon in the top left of Stately Studio.
2. From the **View** submenu, toggle <Check size={18} /> **Show/hide UI** to show and hide the UI.

You can also use the <kbd>Command</kbd>/<kbd>Ctrl</kbd> + <kbd>.</kbd> keyboard shortcut to show and hide the UI.

<Callout>
  We’ve got [keyboard shortcuts](keyboard-shortcuts) for many of the view controls.
</Callout>

Below is a preview of a machine *with* the UI hidden.

{/* <p>
  <ThemedImage
    alt="Starter machine in Stately Studio. The only panel visible is the top left corner with the Stately logo and the machine name."
    sources={{
      light: '/canvas-view-controls/hide-ui.png',
      dark: '/canvas-view-controls/hide-ui-dm.png',
    }}
  />
  </p> */}

<img alt="Starter machine in Stately Studio. The only panel visible is the top left corner with the Stately logo and the machine name." src={__img1} placeholder="blur" />

Below is a preview of a machine *without* the UI hidden.

{/* <p>
  <ThemedImage
    alt="Starter machine in Stately Studio. All the panels are visible, including the top bar, view options, left sidebar, and right tool menu."
    sources={{
      light: '/canvas-view-controls/show-ui.png',
      dark: '/canvas-view-controls/show-ui-dm.png',
    }}
  />
  </p> */}

<img alt="Starter machine in Stately Studio. All the panels are visible, including the top bar, view options, left sidebar, and right tool menu." src={__img2} placeholder="blur" />

## View controls

* <Undo size={18} /> Undo
* <Redo size={18} /> Redo
* <Scan size={18} /> Center in view: Center currently selected item or entire machine
  in view.
* Zoom in %: Current zoom level. Press to open the zoom menu.

<Callout>
  [Find out more about light mode, dark mode, and translucency in user preferences](user-preferences).
</Callout>

## Zoom to selection

You can use **Zoom to selection** from right-click on any transition or state on the canvas or in the <ListTree size={18} /> **Structure** panel.


# Cheatsheet (/docs/cheatsheet)

Use this cheatsheet to quickly look up the syntax for XState v5.

## Installing XState

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate
    ```
  </Tab>
</Tabs>

[Read more on installing XState](installation).

## Creating a state machine

```ts
import { setup, createActor, assign } from 'xstate';

const machine = setup({
  /* ... */
}).createMachine({
  id: 'toggle',
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      entry: assign({
        count: ({ context }) => context.count + 1,
      }),
      on: {
        toggle: { target: 'inactive' },
      },
    },
    inactive: {
      on: {
        toggle: { target: 'active' },
      },
    },
  },
});

const actor = createActor(machine);
actor.subscribe((snapshot) => {
  console.log(snapshot.value);
});

actor.start();
// logs 'active' with context { count: 1 }

actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 1 }
actor.send({ type: 'toggle' });
// logs 'active' with context { count: 2 }
actor.send({ type: 'toggle' });
// logs 'inactive' with context { count: 2 }
```

[Read more about the actor model](actor-model).

## Creating promise logic

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(async () => {
  const response = await fetch('https://dog.ceo/api/breeds/image/random');
  const dog = await response.json();
  return dog;
});

const actor = createActor(promiseLogic);

actor.subscribe((snapshot) => {
  console.log(snapshot);
});

actor.start();
// logs: {
//   message: "https://images.dog.ceo/breeds/kuvasz/n02104029_110.jpg",
//   status: "success"
// }
```

[Read more about promise actor logic](/docs/actors#actors-as-promises).

## Creating transition logic

A transition function is just like a reducer.

```ts
import { fromTransition, createActor } from 'xstate';

const transitionLogic = fromTransition(
  (state, event) => {
    switch (event.type) {
      case 'inc':
        return {
          ...state,
          count: state.count + 1,
        };
      default:
        return state;
    }
  },
  { count: 0 }, // initial state
);

const actor = createActor(transitionLogic);

actor.subscribe((snapshot) => {
  console.log(snapshot);
});

actor.start();
// logs { count: 0 }

actor.send({ type: 'inc' });
// logs { count: 1 }
actor.send({ type: 'inc' });
// logs { count: 2 }
```

[Read more about transition actors](/docs/actors#fromtransition).

## Creating observable logic

```ts
import { fromObservable, createActor } from 'xstate';
import { interval } from 'rxjs';

const observableLogic = fromObservable(() => interval(1000));

const actor = createActor(observableLogic);

actor.subscribe((snapshot) => {
  console.log(snapshot);
});

actor.start();
// logs 0, 1, 2, 3, 4, 5, ...
// every second
```

[Read more about observable actors](/docs/actors#fromobservable).

## Creating callback logic

```ts
import { fromCallback, createActor } from 'xstate';

const callbackLogic = fromCallback(({ sendBack, receive }) => {
  const i = setTimeout(() => {
    sendBack({ type: 'timeout' });
  }, 1000);

  receive((event) => {
    if (event.type === 'cancel') {
      console.log('canceled');
      clearTimeout(i);
    }
  });

  return () => {
    clearTimeout(i);
  };
});

const actor = createActor(callbackLogic);

actor.start();

actor.send({ type: 'cancel' });
// logs 'canceled'
```

[Read more about callback actors](/docs/actors#fromcallback).

## Parent states

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  /* ... */
}).createMachine({
  id: 'parent',
  initial: 'active',
  states: {
    active: {
      initial: 'one',
      states: {
        one: {
          on: {
            NEXT: { target: 'two' },
          },
        },
        two: {},
      },
      on: {
        NEXT: { target: 'inactive' },
      },
    },
    inactive: {},
  },
});

const actor = createActor(machine);

actor.subscribe((snapshot) => {
  console.log(snapshot.value);
});

actor.start();
// logs { active: 'one' }

actor.send({ type: 'NEXT' });
// logs { active: 'two' }

actor.send({ type: 'NEXT' });
// logs 'inactive'
```

[Read more about parent states](parent-states).

## Actions

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  actions: {
    activate: () => {
      /* ... */
    },
    deactivate: () => {
      /* ... */
    },
    notify: (_, params: { message: string }) => {
      /* ... */
    },
  },
}).createMachine({
  id: 'toggle',
  initial: 'active',
  states: {
    active: {
      // [!code highlight:1]
      entry: { type: 'activate' },
      // [!code highlight:1]
      exit: { type: 'deactivate' },
      on: {
        toggle: {
          target: 'inactive',
          // [!code highlight:1]
          actions: [{ type: 'notify' }],
        },
      },
    },
    inactive: {
      on: {
        toggle: {
          target: 'active',
          // [!code highlight:9]
          actions: [
            // action with params
            {
              type: 'notify',
              params: {
                message: 'Some notification',
              },
            },
          ],
        },
      },
    },
  },
});

const actor = createActor(
  machine.provide({
    actions: {
      notify: (_, params) => {
        console.log(params.message ?? 'Default message');
      },
      activate: () => {
        console.log('Activating');
      },
      deactivate: () => {
        console.log('Deactivating');
      },
    },
  }),
);

actor.start();
// logs 'Activating'

actor.send({ type: 'toggle' });
// logs 'Deactivating'
// logs 'Default message'

actor.send({ type: 'toggle' });
// logs 'Some notification'
// logs 'Activating'
```

[Read more about actions](actions).

## Guards

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  // [!code highlight:9]
  guards: {
    canBeToggled: ({ context }) => context.canActivate,
    isAfterTime: (_, params) => {
      const { time } = params;
      const [hour, minute] = time.split(':');
      const now = new Date();
      return now.getHours() > hour && now.getMinutes() > minute;
    },
  },
  actions: {
    notifyNotAllowed: () => {
      console.log('Cannot be toggled');
    },
  },
}).createMachine({
  id: 'toggle',
  initial: 'active',
  context: {
    canActivate: false,
  },
  states: {
    inactive: {
      on: {
        toggle: [
          {
            target: 'active',
            // [!code highlight:1]
            guard: 'canBeToggled',
          },
          {
            actions: 'notifyNotAllowed',
          },
        ],
      },
    },
    active: {
      on: {
        toggle: {
          // Guard with params
          // [!code highlight:1]
          guard: { type: 'isAfterTime', params: { time: '16:00' } },
          target: 'inactive',
        },
      },
      // ...
    },
  },
});

const actor = createActor(machine);

actor.start();
// logs 'Cannot be toggled'
```

[Read more about guards](guards).

## Invoking actors

```ts
import { setup, fromPromise, createActor, assign } from 'xstate';

const loadUserLogic = fromPromise(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
  const user = await response.json();
  return user;
});

const machine = setup({
  // [!code highlight:1]
  actors: { loadUserLogic },
}).createMachine({
  id: 'toggle',
  initial: 'loading',
  context: {
    user: undefined,
  },
  states: {
    loading: {
      // [!code highlight:16]
      invoke: {
        id: 'loadUser',
        src: 'loadUserLogic',
        onDone: {
          target: 'doSomethingWithUser',
          actions: assign({
            user: ({ event }) => event.output,
          }),
        },
        onError: {
          target: 'failure',
          actions: ({ event }) => {
            console.log(event.error);
          },
        },
      },
    },
    doSomethingWithUser: {
      // ...
    },
    failure: {
      // ...
    },
  },
});

const actor = createActor(machine);

actor.subscribe((snapshot) => {
  console.log(snapshot.context.user);
});

actor.start();
// eventually logs:
// { id: 1, name: 'Leanne Graham', ... }
```

[Read more about invoking actors](invoke).

## Spawning actors

```ts
import { setup, fromPromise, createActor, assign } from 'xstate';

const loadUserLogic = fromPromise(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
  const user = await response.json();
  return user;
});

const machine = setup({
  actors: {
    loadUserLogic,
  },
}).createMachine({
  context: {
    userRef: undefined,
  },
  on: {
    loadUser: {
      actions: assign({
        // [!code highlight:1]
        userRef: ({ spawn }) => spawn('loadUserLogic'),
      }),
    },
  },
});

const actor = createActor(machine);
actor.subscribe((snapshot) => {
  const { userRef } = snapshot.context;
  console.log(userRef?.getSnapshot());
});
actor.start();

actor.send({ type: 'loadUser' });
// eventually logs:
// { id: 1, name: 'Leanne Graham', ... }
```

[Read more about spawning actors](spawn).

## Input and output

```ts
import { setup, createActor } from 'xstate';

const greetMachine = setup({
  types: {
    context: {} as { message: string },
    input: {} as { name: string },
  },
}).createMachine({
  // [!code highlight:3]
  context: ({ input }) => ({
    message: `Hello, ${input.name}`,
  }),
  entry: ({ context }) => {
    console.log(context.message);
  },
});

const actor = createActor(greetMachine, {
  // [!code highlight:3]
  input: {
    name: 'David',
  },
});

actor.start();
// logs 'Hello, David'
```

[Read more about input](input).

## Invoking actors with input

```ts
import { setup, createActor, fromPromise } from 'xstate';

const loadUserLogic = fromPromise(async ({ input }) => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${input.id}`,
  );
  const user = await response.json();
  return user;
});

const machine = setup({
  actors: {
    loadUserLogic,
  },
}).createMachine({
  initial: 'loading user',
  states: {
    'loading user': {
      invoke: {
        id: 'loadUser',
        src: 'loadUserLogic',
        // [!code highlight:3]
        input: {
          id: 3,
        },
        onDone: {
          actions: ({ event }) => {
            console.log(event.output);
          },
        },
      },
    },
  },
});

const actor = createActor(machine);

actor.start();
// eventually logs:
// { id: 3, name: 'Clementine Bauch', ... }
```

[Read more about invoking actors with input](input.mdx#invoking-actors-with-input).

## Types

```ts
import { setup, fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
  /* ... */
});

const machine = setup({
  types: {
    context: {} as {
      count: number;
    };
    events: {} as
      | { type: 'inc'; }
      | { type: 'dec' }
      | { type: 'incBy'; amount: number };
    actions: {} as
      | { type: 'notify'; params: { message: string } }
      | { type: 'handleChange' };
    guards: {} as
      | { type: 'canBeToggled' }
      | { type: 'isAfterTime'; params: { time: string } };
    children: {} as {
      promise1: 'someSrc';
      promise2: 'someSrc';
    };
    delays: 'shortTimeout' | 'longTimeout';
    tags: 'tag1' | 'tag2';
    input: number;
    output: string;
  },
  actors: {
    promiseLogic
  }
}).createMachine({
  // ...
});
```


# Colors (/docs/colors)

import { Info, Star } from 'lucide-react';

You can highlight your machine’s state and event nodes with colors. You can use colors however you like; some ideas include:

* Color coding groups or types of states or events
* Emphasizing success or error states
* Making your machines match your brand

<Callout>
  Colors are a premium feature of Stately Studio. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/7b5d60dc-94b0-4a1e-9ade-ea644b8cc35f?machineId=3b1987cf-4346-4a10-bb03-2c9f0c363bda" />

<Callout>
  A note on accessibility: not everyone perceives color the same way, and as many as 8% of men and 0.5% of women are [color blind](https://www.nei.nih.gov/learn-about-eye-health/eye-conditions-and-diseases/color-blindness). Ensure your statecharts are inclusive by using color to emphasize or decorate your machines and [do not use color as the only way to convey information](https://www.w3.org/WAI/WCAG21/Understanding/use-of-color.html).
</Callout>

## Change the color of a state or event

All states and events have a default color. In dark mode, the default color is grey. In light mode, the default color is white.

When you hover over the color options, a preview of the color will be shown on the selected state or event.

Colors are faded out in Simulate mode to make the current state and possible events easier to distinguish.

### On the canvas

1. Select the state or event whose color you want to change.
2. Choose the circular color swatch in the center of the Edit menu to open the **Color state** or **Color event** options.
3. Choose your desired color from the available color options.

### Use the details panel

1. Select the state or event whose color you want to change.
2. Open the <Info size={18} /> **Details** panel from the right tool menu.
3. Choose the circular color swatch in the top right of the <Info size={18} /> **Details** panel to open the **Color state** or **Color event** options.
4. Choose your desired color from the available color options.

## Available colors

<EmbedMachine name="Colors" embedURL="https://stately.ai/registry/editor/embed/7b5d60dc-94b0-4a1e-9ade-ea644b8cc35f?machineId=34da5c64-3aed-455d-883b-8b6d5ac4c9d5" />

* Default: grey in dark mode, white in light mode
* Purple
* Pink
* Red
* Orange
* Yellow
* Green
* Blue


# Context (/docs/context)

In XState, `context` is how you store data in a state machine [actor](actors).

The `context` property is available in all states and used to store data relevant to an actor. The `context` object is immutable, so you cannot directly modify it. Instead, for state machine logic, you can use the `assign(...)` action to update `context`.

The `context` property is *optional*; if the state machine only specifies [finite states](finite-states) and no external contextual data, it may not need `context`.

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  // Initialize the state machine with context
  context: {
    feedback: 'Some feedback',
  },
});

const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.context.feedback);
});

feedbackActor.start();
// logs 'Some feedback'
```

## Initial context

Set the initial context of a machine in the `context` property of the machine config:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  context: {
    feedback: 'Some feedback',
    rating: 5,
    // other properties
  },
});
```

The object you pass to `context` will be the initial `context` value for any actor created from this machine.

<Callout>
  Do not mutate the `context` object. Instead, you should use the `assign(...)` action to update `context` immutably. If you mutate the `context` object, you may get unexpected behavior, such as mutating the `context` of other actors.
</Callout>

### Lazy initial context

Context can be initialized lazily by passing a function that returns the initial `context` value:

```ts
const feedbackMachine = createMachine({
  context: () => ({
    feedback: 'Some feedback',
    createdAt: Date.now(),
  }),
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().context.createdAt);
// logs the current timestamp
```

Lazy initial context is evaluated per actor, so each actor will have its own `context` object.

### Input

You can provide input data to a machine’s initial `context` by passing an `input` property to the `createActor(machine, { input })` function and using the `input` property from the first argument in the `context` function:

```ts
import { setup, createActor } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as {
      feedback: string;
      rating: number;
    },
    // [!code highlight:3]
    input: {} as {
      defaultRating: number;
    },
  },
}).createMachine({
  // [!code highlight:1]
  context: ({ input }) => ({
    feedback: '',
    // [!code highlight:1]
    rating: input.defaultRating,
  }),
});

const feedbackActor = createActor(feedbackMachine, {
  // [!code highlight:3]
  input: {
    defaultRating: 5,
  },
}).start();

console.log(feedbackActor.getSnapshot().context.rating);
// logs 5
```

Learn more about [input](input).

## Updating context with `assign(...)`

Use the `assign(...)` action in a transition to update context:

```ts
import { createMachine, assign, createActor } from 'xstate';

const feedbackMachine = createMachine({
  context: {
    feedback: 'Some feedback',
  },
  on: {
    'feedback.update': {
      actions: assign({
        feedback: ({ event }) => event.feedback,
      }),
    },
  },
});

const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.context.feedback);
});

feedbackActor.start();

// logs 'Some feedback'

feedbackActor.send({
  type: 'feedback.update',
  feedback: 'Some other feedback',
});

// logs 'Some other feedback'
```

## Context and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `context` of your machine in the `types.context` property of the actor setup..

```ts
import { setup } from 'xstate';

const machine = setup({
  types: {} as {
    // [!code highlight:4]
    context: {
      feedback: string;
      rating: number;
    };
  },
}).createMachine({
  // Initial context
  context: {
    feedback: '',
    rating: 5,
  },
  entry: ({ context }) => {
    context.feedback; // string
    context.rating; // number
  },
});
```

## Context cheatsheet

Use our XState context cheatsheet below to get started quickly.

### Cheatsheet: initial context

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  context: {
    feedback: '',
  },
});
```

### Cheatsheet: lazy initial context

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  context: () => ({
    feedback: '',
    createdAt: Date.now(),
  }),
});
```

### Cheatsheet: updating context with `assign(...)`

```ts
import { createMachine, assign } from 'xstate';

const machine = createMachine({
  context: {
    feedback: '',
  },
  on: {
    'feedback.update': {
      actions: assign({
        feedback: ({ event }) => event.feedback,
      }),
    },
  },
});
```

### Cheatsheet: input

```ts
import { setup, createActor } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as {
      feedback: string;
      rating: number;
    },
    // [!code highlight:3]
    input: {} as {
      defaultRating: number;
    },
  },
}).createMachine({
  // [!code highlight:1]
  context: ({ input }) => ({
    feedback: '',
    // [!code highlight:1]
    rating: input.defaultRating,
  }),
});

const feedbackActor = createActor(feedbackMachine, {
  // [!code highlight:3]
  input: {
    defaultRating: 5,
  },
}).start();
```


# Delayed (after) transitions (/docs/delayed-transitions)

**Delayed transitions** are transitions that are triggered after a set amount of time. Delayed transitions are useful for building timeouts and intervals into your application logic. If another event occurs before the end of the timer, the transition doesn’t complete.

Delayed transitions are defined on the `after` property of a state node, either as a number (measured in milliseconds) or as a string that references a delay defined in the `delays` setup object.

<Callout>
  You can easily visualize and simulate delayed transitions in Stately’s editor. [Read more about delayed transitions in Stately’s editor](/docs/editor-states-and-transitions/#delayed-after-transitions).
</Callout>

```ts
import { createMachine } from 'xstate';

const pushTheButtonGame = createMachine({
  initial: 'waitingForButtonPush',
  states: {
    waitingForButtonPush: {
      // [!code highlight:6]
      after: {
        5000: {
          target: 'timedOut',
          actions: 'logThatYouGotTimedOut',
        },
      },
      on: {
        PUSH_BUTTON: {
          actions: 'logSuccess',
          target: 'success',
        },
      },
    },
    success: {},
    timedOut: {},
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=78c59862-fc40-4b1e-8f9c-42f1b2ddc410" title="Push the button game" />

<Callout>
  Watch our [“Delayed (after) transitions” video on YouTube](https://www.youtube.com/watch?v=5RE_eazRhrw\&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ\&index=12) (1m17s).
</Callout>

## Delays

You can define delays in a few ways: [inlined](#inlined-delays), [referenced](#referenced-delays), and as an expression.

## Inlined delays

You can define an inlined delay by specifying the delay time (in milliseconds) directly:

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'idle',
  states: {
    idle: {
      after: {
        1000: { target: 'nextState' },
      },
    },
    nextState: {},
  },
});
```

This will transition to the `nextState` state after 1000ms.

## Referenced delays

You can also define referenced delays by specifying a string delay key, and providing the actual delay time separately.

For example:

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:3]
  delays: {
    timeout: 1000,
  },
}).createMachine({
  initial: 'idle',
  states: {
    idle: {
      after: {
        // [!code highlight:1]
        timeout: { target: 'nextState' },
      },
    },
    nextState: {},
  },
});
```

## Dynamic delays

Delays can also be dynamically defined as a function that returns the delay time in milliseconds:

```ts
import { setup, assign } from 'xstate';

const machine = setup({
  types: {
    context: {} as {
      attempts: number;
    },
  },
  // [!code highlight:5]
  delays: {
    timeout: ({ context }) => {
      return context.attempts * 1000;
    },
  },
}).createMachine({
  initial: 'attempting',
  states: {
    attempting: {
      after: {
        // [!code highlight:4]
        timeout: {
          actions: assign({ attempts: ({ context }) => context.attempts + 1 }),
          target: 'attempting',
        },
      },
    },
    // ...
  },
});
```

## Lifecycle

Delayed transition timers are canceled when the state is exited.

## Testing

* Simulated clock

## Delayed transitions and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `delays` of your machine by setting up the the delays in the `setup()` function:

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:5]
  delays: {
    shortTimeout: 1000,
    longTimeout: 5000,
    eventually: 10_000,
  },
}).createMachine({
  after: {
    shortTimeout: {
      /* ... */
    },
  },
});
```

## Delayed transitions cheatsheet

Use our XState delayed transitions cheatsheet below to get started quickly.

```ts
import { createMachine } from 'xstate';

createMachine({
  after: {
    DELAY: {
      /* ... */
    },
  },
}).provide({
  delays: {
    DELAY: 1000, // or expression
  },
});
```


# Descriptions (/docs/descriptions)

import { AlignLeft, Info, Plus } from 'lucide-react';

You can add descriptions to state and event nodes to describe their purpose and share related notes with your team. Descriptions support markdown formatting, including links and images.

You can only add one description for each state or event. The machine object will include your descriptions in the state or event’s `description` when you export your statecharts to JSON.

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=574ede8a-a328-40fe-a4f9-c80fffb2c30a" />

In the video player above, the text “The video player should be in full-screen mode” is a description of the *Opened* event.

## Add a description to a state, event, or transition

Use the <Plus size={18} /> plus icon button and choose <AlignLeft size={18} /> **Description** from the menu to add a description block to your selected state, event, or transition.

You can also add descriptions from the <Info size={18} /> **Details** panel in the right tool menu.

## Delete a description from a state, event, or transition

Select the state, event, or transition whose description you want to delete, open the <Info size={18} /> **Details** panel, and delete the contents of the description text area.


# Design mode (/docs/design-mode)



import { ClipboardCheck, Code, Database, History, Info, ListTree, Play, UploadCloud, HelpCircle } from 'lucide-react';

Design your state machine flows in Design Mode.

You can add new [states](states) by double-clicking anywhere on the canvas, and add [transitions and events](transitions) from the handles on each side of the states.

{/* <p>
  <ThemedImage
    alt="Numbered screenshot of Stately editor’s Design mode features. Described by list below."
    sources={{
      light: '/design-mode-signed-in.png',
      dark: '/design-mode-signed-in-dm.png',
    }}
  />
  </p> */}

<img alt="Numbered screenshot of Stately editor’s Design mode features. Described by list below." src={__img0} placeholder="blur" />

1. **Editor menu**: access common Stately shortcuts and view options.
2. **Left drawer switch**: open and close the left drawer which contains the machines list.
3. Current [project](projects).
4. Current machine.
5. <Code size={18} /> **Code** panel.
6. <ListTree size={18} /> **Structure** panel.
7. **Share** button: Open the share options.
8. <Play size={18} /> [**Simulate**](simulate-mode).
9. <UploadCloud size={18} /> [**Deploy**](stately-sky-getting-started).
10. <Info size={18} /> **Details** panel for the currently selected machine, state,
    or transition.
11. <Code size={18} /> **Events** schema panel.
12. <Database size={18} /> [**Context**](context) schema panel.
13. <ClipboardCheck size={18} /> [**Tests**](/docs/generate-test-paths) panel.
14. Canvas: where your state machine is designed and displayed.
15. [Canvas tools](canvas-view-controls).
16. <History size={18} /> Current [version](versions) displayed on the canvas.
17. [Project visibility](projects).
18. [Canvas view controls](canvas-view-controls).
19. <HelpCircle size={18} /> Help button: open and close the help drawer.


# Developer tools (/docs/developer-tools)

<Callout type="warning">
  The XState developer tools currently only work for XState version 4. Typegen is not supported in XState version 5.
</Callout>

Find more about our [XState CLI (Command Line Interface)](#xstate-cli-command-line-interface) below. We plan to make extensions for more IDEs (Integrated Development Environments) in the future.

<Callout>
  [Read about our XState VS Code extension on its own page](xstate-vscode-extension).
</Callout>

## XState CLI (Command Line Interface)

The [@xstate/cli (Command Line Interface) package](https://github.com/statelyai/xstate-tools/tree/master/apps/cli) contains commands for running typegen. The package is small right now, but we plan to add more features.

### Installation

Run `npm install @xstate/cli`.

### Commands

#### `xstate typegen <files>`

Use the following command to run the typegen against a glob.

`xstate typegen "src/**/*.ts?(x)"`

Running typegen will scan every targeted file and generate a typegen file to accompany it. It will also import the typegen into your file, as described in [our typegen documentation](typegen).

<Callout type="warning">
  Ensure you wrap your glob in quotes for correct execution. If you don’t wrap the glob in quotes, it will be interpreted as a list of files, not a glob, which will give unexpected results.
</Callout>

#### Options

`xstate typegen "src/**/*.ts?(x)" --watch`

Runs the task on a watch, monitoring for changed files and running the typegen script against them.


# Discover (/docs/discover)



import { GitFork, MoreHorizontal } from 'lucide-react';

Are you seeking inspiration for your machine? Or do you want to learn how somebody else models their machines? The [Discover page](https://stately.ai/registry/discover) lists all the public machines created in Stately Studio.

{/* <p>
  <ThemedImage
    alt="Stately Studio Discover page showing the search results for “auth”, filtered by editor machines under 10 states, showing 173 results."
    sources={{
      light: '/discover.png',
      dark: '/discover-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Discover page showing the search results for “auth”, filtered by editor machines under 10 states, showing 173 results." src={__img0} placeholder="blur" />

With the search feature, you can quickly filter your results by the number of states in a machine and whether the machine’s creator used [Stately Studio’s editor](https://stately.ai/editor) or our older [Viz](https://stately.ai/viz) tool. Each machine listed details its creator, number of states, and a link to view and edit the machine in the editor.

## Export and fork public machines

When viewing somebody else’s machine, you can’t make changes. You can fork the machine to create an editable copy inside your **My Projects**.

### Export a public machine

Choose **Export code** from the <MoreHorizontal size={18} /> triple dot contextual menu alongside the machine name in the machines list.

Read [more about exporting your machines as code](export-as-code).

### Fork a public machine

<Callout>
  You must be signed into the Stately Studio to fork a machine.
</Callout>

Choose <GitFork size={18} /> **Fork to project** from the <MoreHorizontal size={18} /> triple dot contextual menu alongside the machine name in the machines list.


# Actions and actors in Stately’s editor (/docs/editor-actions-and-actors)

import { Code, Info, Plus, Zap, Edit, Trash, Backspace, MoreHorizontal, PlayCircle } from 'lucide-react';

# Actions and actors

While the state machine is running, it can execute effects called actions. Actions are executed when a transition is triggered. Actions are “fire-and-forget effects”; once the machine has fired the action, it continues processing the transition and forgets the action. You can also fire actions when a state is entered or exited. [Read more about actions](/docs/actions).

State machines can invoke actors as longer-running processes that can receive events, send events, and change their behavior based on the events they receive. You can invoke actors on entry to a state and stop on exit. [Read more about actors](actors).

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?code=834448b4-3b8b-4de9-8b2e-353fd2b342de&mode=design&machineId=000b2694-2267-413c-8477-988cc750d581" title="Demo of actions and actors" />

## Add actions

* Select a state or transition and use <Zap size={18} /> **Action**.
* Select a state and use <Plus size={18} /> **Entry action** or **Exit action**.
* Select a state or transition, open the <Info size={18} /> **Details** panel from the right tool menu, and use the <Plus size={18} /> **Effect** or <Plus size={18} /> **Action** button and choose **Add entry action**, **Add exit action**, or **Add action**.

Use the <Edit size={18} /> edit icon button to open the <Code size={18} /> **Sources** panel and add custom implementation code. Actions are created as custom actions by default, but you can also use [XState built-in actions](#xstate-built-in-actions).

To remove an action, use the <kbd>Backspace</kbd> key, *right-click* and choose **Delete**, or use the <Trash size={18} /> delete icon button in the **Details** panel.

### Add action parameters

You can add action parameters by selecting the action and using add <Plus size={18} /> **parameter**.

## Add invoked actors

You can invoke multiple actors on a single state. Top-level final states cannot have invoked actors. [Read more about invoking actors](invoke).

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=66f77051-089e-4b0a-9fa9-42e1f7598135" name="video player" />

In the video player above, the *startVideo* actor is invoked when the video player is in the *Opened* state.

### Invoke actors on a state

* Select a state and use <PlayCircle size={18} /> **Invoke**.
* Select a state, open the state <Info size={18} /> **Details** panel from the right tool menu, and use the <Plus size={18} /> **Effect** button and choose **Add invoked actor**.

Use the <Edit size={18} /> edit icon button to open the <Code size={18} /> **Sources** panel and enter the actor’s source logic.

Provide your actor with an ID so it can be used with the [`sendTo` or `stop` actions](#xstate-built-in-actions) to stop and send events to the actor. You can add actor input by selecting the actor and using add <Plus size={18} /> **input property**.

To remove an actor, use the <kbd>Backspace</kbd> key, *right-click* and choose **Delete**, or use the <Trash size={18} /> delete icon button in the **Details** panel.

#### Invoke done and invoke error events

**Invoke done events** and **invoke error events** transition from a state once its invocation has been completed or returns an error. The source state must have an invoked actor to create an invoke done or invoke error event.

* Select the state with an invoked actor and create a new transition from that state. The first new transition will be created as an invoke done event.
* Subsequent new transitions will be created as invoke error events.

To change an invoke done or invoke error event back into a regular transition, Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* the transition, and from **Event type**, choose **Always**.

### XState built-in actions

You can use the following built-in XState actions from the logic templates in the <Code size={18} /> **[Sources](sources)** panel, which will be formatted in your [exported code](export-as-code). The options are:

* [assign](/docs/actions/#assign-action): assigns data to the state context.

* [raise](/docs/actions/#raise-action): raises an event that is received by the same machine.

* [log](/docs/actions/#log-action): an easy way to log messages to the console.

* [sendTo](/docs/actions/#send-to-action): sends an event to a specific actor.

* [stop](/docs/actions/#stop-action): stops a child actor.

* [Read more about actions in XState](actions).

* [Read more about actors in XState](actors).

### Spawning actors in Stately’s editor

*Coming soon*


# Context and meta in Stately’s editor (/docs/editor-context-and-meta)

* Coming soon… setting initial context values
* Coming soon… updating context with assign
* Coming soon… JS/TS export


# States and transitions in Stately’s editor (/docs/editor-states-and-transitions)

import { MoreHorizontal, Plus, Info, AlertTriangle, Star, Code, PlayCircle } from 'lucide-react';

# States and transitions

State machines help us model how a process goes from state to state when an event occurs. At their most basic, state machines are made up of these states, events, and the transitions between them.

<Callout>
  Want to learn more about the concepts of state machines? [Check out our introduction to state machines and statecharts](state-machines-and-statecharts).
</Callout>

## States

In Stately’s editor, the rounded rectangle boxes are states. There are a few different types of states:

* **Normal** states don’t have any special properties.
* [**Initial states**](#initial-states) are the first states the machine enters when it starts.
* [**Final states**](#final-states) are the last states the machine enters before it stops.
* [**Parent states**](#parent-and-child-states) can contain more states, known as child states.
* [**Parallel states**](#parallel-states) are parent states that have multiple child states that are all active at the same time.
* [**History states**](#history-states) are parent states that remember which child state was active when the parent state was exited and re-enter that child state when the parent state is re-entered.

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=61fa1504-a92c-4730-a87d-3ee9dd5d6e33&mode=design" title="Demoing states" />

You can invoke other state machines or actors on a state, or trigger an action when a state is entered or exited. [Read more about actions and actors in Stately’s editor](editor-actions-and-actors).

In Stately’s editor, you can also add [descriptions](descriptions), [colors](colors), and [tags](tags) to states.

### Create a state

* Double-click anywhere on the canvas to create a new state there.
* Select an existing state and use <Plus size={18} /> **child state** to add a new state inside.

#### Delete a state

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* on a selected state, and choose **Delete** to delete the selected state.
* Use the <kbd>Backspace</kbd> key to delete the selected state.

### Parent and child states

States can contain more states, also known as child states. These child states are only active when the parent state is active. [Read more about parent and child states](parent-states).

To add a child state:

* Select an existing state and use <Plus size={18} /> **child state** to add a new state inside.
* If a state already contains child states, you can double-click inside the parent state to create another child state.
* Copy a state or group of states and transitions, and paste them onto their new parent state.
* Reparent a child state by selecting the child and choosing a new parent state from inside the state <Info size={18} /> **Details** panel.

### Initial states

When a state machine starts, it enters the **initial state** first. In Stately’s editor, the filled circle with an arrow icon represents the initial state.

Machines can only have one top-level initial state. Each parent state has its own initial state. If you have unreachable states, it might be because there is no initial state. [Read more about initial states](initial-states).

To set a state as the initial state:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a state and choose Mark as initial state.
* Select the parent state, open the state <Info size={18} /> **Details** panel, and choose the desired initial state from the **Initial state** menu.

### Final states

When a machine reaches the final state, it can no longer receive any events, and anything running inside it is canceled and cleaned up.

To turn a state into a final state:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a state, and from **State type**, choose **Final state**.
* Select the state, open the state <Info size={18} /> **Details** panel, and choose the **Final** state option.

If you want your machine to transition from a parent state when its final child state is reached, use a [state done event](#state-done-event).

### Parallel states

In statecharts, a parallel state is a state that has multiple child states (also known as **regions**) that are all active at the same time. [Read more about parallel states](parallel-states). A dashed line borders each region.

To turn a parent state into a parallel state:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a parent state, and from **State type**, choose **Parallel state**.
* Select the parent state, open the state <Info size={18} /> **Details** panel, and choose the **Parallel** state option.

### History states

A history state remembers the last child state that was active before its parent state was exited. When a transition from outside the parent state targets a history state, the remembered child state is entered. [Read more about history states](history-states).

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a state, and from **State type**, choose **History state**.
* Select the parent state, open the state <Info size={18} /> **Details** panel, and choose the **History** state option.

### Unreachable states

A <AlertTriangle size={18} /> warning icon indicates an unreachable state. The state is unreachable because it isn’t connected to the [initial state](#initial-states) by a [transition](#transitions-and-events).

## Transitions and events

A machine moves from state to state through transitions. Transitions are caused by events; when an event happens, the machine transitions to the next state.

In Stately’s editor, the arrows are transitions, and the rounded rectangles on the arrow’s lines are events. Each transition has a *source* state, which comes before the transition, and a *target* state, which comes after the transition. The transition’s arrow starts from the source state and points to the target state.

There are a few different types of transitions:

* **Normal** transitions are triggered by an event.
* [**Guarded transitions**](#add-guards) are triggered by an event, but only if a specified condition is met.
* [**Delayed transitions**](#delayed-after-transitions) (also known as *after* transitions) are triggered by an internal XState event, but only after a specified time interval.
* [**Eventless transitions**](#eventless-always-transitions) (also known as *always* transitions) are triggered by a timer or other condition, and don’t have an event.

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?code=834448b4-3b8b-4de9-8b2e-353fd2b342de&mode=design&machineId=50c0ee06-8297-4b79-95d1-b1abf779d757" title="Demoing transitions" />

### Create a transition and event

* Click a handle on any state to create a transition, event, and target state.
* Drag from a source state’s handle to the target state’s handle to connect them with a transition and event.

#### Delete a transition

Deleting a state will also delete any transitions with that state as a source.

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* on a selected transition, and choose **Delete** to delete the selected transition.
* Use the <kbd>Backspace</kbd> key to delete the selected transition.

### Changing transition source and target

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* the transition and choose **Switch source and target**.
* Drag the transition handle from one state to a different state.
* Select a transition, open the transition <Info size={18} /> **Details** panel from the right tool menu, and choose a new source or target state from the dropdown menus.

### Delayed (after) transitions

**Delayed transitions** are transitions that only happen after a set amount of time. If another event occurs before the end of the timer, the transition doesn’t complete. [Read more about delayed transitions](delayed-transitions).

In Stately’s editor, delayed transitions are labeled “after.” Delayed transitions have a default time interval of 500ms (0.5 seconds).

To make a transition into a delayed transition:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition, and from **Event type**, choose **After**.
* Select an event, open the transition <Info size={18} /> **Details** panel from the right tool menu, and choose **After** from the **Trigger** dropdown menus.

Your delay time interval will be displayed in a human-readable format on hover. For example, 15000ms will be displayed as 15 seconds.

To set the delay interval:

* Use the text input on the delayed transition to specify the interval in milliseconds (ms).
* Select the transition delayed transition, open the transition <Info size={18} /> **Details** panel from the right tool menu, and specify the interval in milliseconds (ms) in the **Delay** text input.

To make a delayed transition into a regular transition, use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition, and from **Event type**, choose **Event**.

### Eventless (always) transitions

**Eventless transitions** are transitions that are *always* taken when the transition is enabled. In Stately’s editor, eventless transitions are labeled “always.” [Read more about eventless transitions](eventless-transitions).

To make a transition into an eventless transition:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition, and from **Event type**, choose **Always**.
* Select an event, open the transition <Info size={18} /> **Details** panel from the right tool menu, and choose **Always** from the **Trigger** dropdown menus.

To make an eventless transition into a regular transition, use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition, and from **Event type**, choose **Event**.

### State done event

Use a state done event to transition from a parent state when its final child state is reached. [Read more about state done events](state-done-events).

To turn an event into a state done event:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* an event, and from **Event type**, choose **State Done event**.

To make a state done event into a regular transition, use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition, and from **Event type**, choose **Event**.

### Self-transitions

A **self-transition** is when a state transitions back to itself, and is useful for changing context and/or executing actions without changing the finite state. You can also use self-transitions to restart a state. [Read more about self-transitions](/docs/transitions/#self-transitions).

To create a self-transition:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* an existing event, and choose **Make self transition**. The transition will be connected back to the source state.
* Select an existing event, open the transition <Info size={18} /> **Details** panel from the right tool menu, and choose the same state from the source and target dropdown menus.

## Add guards

A **guarded transition** is a transition that is only enabled if its condition is evaluated to be `true`. [Read more about guarded transitions](guards).

In Stately’s editor, guards are numbered in the order they are checked and labeled with “if” or “else if” along with their condition. Multiple guards on the same events are connected with a dotted line.

To add a guard:

* Select a transition and use the <Plus size={18} /> **Add guard** to add a new guard to the transition. Use the text input to add the guard’s condition.
* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* a transition and use **Add guard** to add a guard to the transition. Use the text input to add the guard’s condition.
* Select an existing transition, open the transition <Info size={18} /> **Details** panel from the right tool menu, and enter the guard’s condition into the **Guard** text input.

To reorder guards:

* Use the <MoreHorizontal size={18} /> triple dot menu or *right-click* the guarded transition, and from the **Reorder guards** menu, choose **Move up** or **Move down**.

To delete a guard, remove its condition from the text input.

You can add implementation code to your guards from the <Code size={18} /> **[Sources](sources)** panel.


# Tags in Stately’s editor (/docs/editor-tags)

import { Plus, Info, PlusSquare } from 'lucide-react';

# Tags

You can add tags to states in Stately’s editor:

* Select the state you want to tag and use <Plus size={18} /> **tag**. Use the text input to enter the tag’s name.
* Select the state you want to tag, open the state <Info size={18} /> **Details** panel, and <Plus size={18} /> **tag** button. Use the text input to enter the tag’s name.
* Use the <PlusSquare size={18} /> plus icon button alongside your recent tag to add more tags.


# Embed machines using their embed URL (/docs/embed)

You can embed your machines anywhere that supports [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe). Embed mode is a focused non-editable view of your machine in Stately Studio’s editor, which can be zoomed and panned.

We love to use embedded machines in our docs! Below is an example of an embedded machine:

<EmbedMachine name="Embed feature machine" embedURL="https://stately.ai/registry/editor/embed/9e7f6b1e-416a-491f-a4a0-bba386a02d8d?mode=Design&machineId=e94d5674-7ab7-4a4e-b7a8-781609bfe523" />

Your machine will only be embeddable if:

* the project visibility is **public**
* the project visibility is **unlisted**

Embed URLs are not available for private machines.

Read [how to change a project’s visibility settings](/docs/projects/#change-a-projects-visibility).

<Callout>
  You can also [get your machine as an image](image) for platforms where `<iframe>` is not supported.
</Callout>

## Copy the embed URL

1. Use the **Share** button in Stately Studio’s top bar to open the Share dialog.
2. Use the **Copy embed URL** button to copy the machine’s embed URL to your clipboard.
3. Use the embed URL in an `<iframe>` to embed the machine anywhere that supports iframes. [Find some example code below](#example-iframe-code).

## Example `<iframe>` code

The following HTML embeds the same machine at the top of this page in dark mode:

```html
<iframe
  loading="lazy"
  src="https://stately.ai/registry/editor/embed/9e7f6b1e-416a-491f-a4a0-bba386a02d8d?mode=Design&machineId=e94d5674-7ab7-4a4e-b7a8-781609bfe523&colorMode=dark"
  style="display: block; width: 100%; aspect-ratio: 6 / 4;"
>
  <a
    href="https://stately.ai/registry/editor/embed/9e7f6b1e-416a-491f-a4a0-bba386a02d8d?mode=Design&machineId=e94d5674-7ab7-4a4e-b7a8-781609bfe523"
  >
    View the <em>Embed feature machine</em> machine in Stately Studio </a
  >.
</iframe>
```

<Callout>
  In the future, we plan to provide configurable embeds with copy-and-paste code.

  If you want us to prioritize improving embed mode, please [upvote it on our feedback page](https://github.com/statelyai/feedback/issues/94).
</Callout>

## URL parameters, including color mode

The embed URL has some of the same parameters as the [machine URL](url).

* **machineId**: the unique ID for the machine. For example, `machineId=491a4c60-5300-4e22-92cf-8a32a8ffffca`
* **mode**: the current machine mode. For example, `mode=Design` or `mode=Simulate`
* **colorMode**: the color mode for the embedded machine. For example, `colorMode=light` or `colorMode=dark`

By default, the color mode will be the same as your chosen Stately Studio color mode. Add `&colorMode=light` or `&colorMode=dark` to the URL to force that color mode.


# Event emitter (/docs/event-emitter)

*Since XState version 5.9.0*

State machines and other types of actor logic in XState have the ability to emit events. This allows external event handlers to be notified of specific events.

With state machines, you can emit events using the `emit(event)` action creator.

```ts
import { setup, emit } from 'xstate';

const machine = setup({
  actions: {
    // [!code highlight:1]
    emitEvent: emit({ type: 'notification' }),
  },
}).createMachine({
  // ...
  on: {
    someEvent: {
      // [!code highlight:1]
      actions: { type: 'emitEvent' },
    },
  },
});

const actor = createActor(machine);

// [!code highlight:3]
actor.on('notification', (event) => {
  console.log('Notification received!', event);
});

actor.start();

actor.send({ type: 'someEvent' });
// Logs:
// "Notification received!"
// { type: "notification" }
```

## Emitting events from actor logic

For promise actors, transition actors, observable actors, and callback actors, you can use the `emit` method from the arguments to emit events.

**Promise actors**

```ts
import { fromPromise } from 'xstate';

// [!code highlight:1]
const logic = fromPromise(async ({ emit }) => {
  // ...
  // [!code highlight:4]
  emit({
    type: 'emitted',
    msg: 'hello',
  });
  // ...
});
```

**Transition actors**

```ts
import { fromTransition } from 'xstate';

// [!code highlight:1]
const logic = fromTransition((state, event, { emit }) => {
  // ...
  // [!code highlight:4]
  emit({
    type: 'emitted',
    msg: 'hello',
  });
  // ...
  return state;
}, {});
```

**Observable actors**

```ts
import { fromObservable } from 'xstate';

// [!code highlight:1]
const logic = fromObservable(({ emit }) => {
  // ...
  // [!code highlight:4]
  emit({
    type: 'emitted',
    msg: 'hello',
  });
  // ...
});
```

**Callback actors**

```ts
import { fromCallback } from 'xstate';

// [!code highlight:1]
const logic = fromCallback(({ emit }) => {
  // ...
  // [!code highlight:4]
  emit({
    type: 'emitted',
    msg: 'hello',
  });
  // ...
});
```

## Emit action creator

The emit action is a special action that *emits* an event to any external event handlers from state machine logic. The emitted event can be statically or dynamically defined:

```ts
import { setup, emit } from 'xstate';

const machine = setup({
  actions: {
    // [!code highlight:5]
    // Emitting a statically-defined event
    emitStaticEvent: emit({
      type: 'someStaticEvent',
      data: 42,
    }),

    // [!code highlight:5]
    // Emitting a dynamically-defined event based on context
    emitDynamicEvent: emit(({ context }) => ({
      type: 'someDynamicEvent',
      data: context.someData,
    })),
  },
}).createMachine({
  // ...
  on: {
    someEvent: {
      actions: [{ type: 'emitStaticEvent' }, { type: 'emitDynamicEvent' }],
    },
  },
});
```

## Event handlers

You can attach event handlers to the actor to listen for emitted events by using `actor.on(event, handler)`:

```ts
const someActor = createActor(someMachine);

// [!code highlight:4]
someActor.on('someEvent', (emittedEvent) => {
  // Handle the emitted event
  console.log(emittedEvent);
});

someActor.start();
```

The `actor.on(…)` method returns a subscription object. You can call `.unsubscribe()` on it to remove the handler:

```ts
const someActor = createActor(someMachine);

// [!code highlight:4]
const subscription = someActor.on('someEvent', (emittedEvent) => {
  // Handle the emitted event
  console.log(emittedEvent);
});

someActor.start();

// ...

// [!code highlight:2]
// Stop listening for events
subscription.unsubscribe();
```

## Wildcard event handlers

You can listen for *any* emitted event by listening for the wildcard `'*'`:

```ts
const someActor = createActor(someMachine);

actor.on('*', (emitted) => {
  console.log(emitted); // Any emitted event
});
```

The `emitted` event will be typed as the union of all possible events that can be emitted from the machine.

## TypeScript

You can strongly type emitted events by defining the emitted event types in the `types.emitted` property of the `setup(…)` function:

```ts
import { setup, emit, createActor } from 'xstate';

const machine = setup({
  types: {
    // [!code highlight:3]
    emitted: {} as
      | { type: 'notification'; message: string }
      | { type: 'error'; error: Error },
    // ...
  },
}).createMachine({
  // ...
  on: {
    someEvent: {
      actions: [
        // [!code highlight:2]
        // Strongly typed emitted event
        emit({ type: 'notification', message: 'Hello' }),
      ],
    },
  },
});

const actor = createActor(machine);

// [!code highlight:4]
// Strongly typed event handler
actor.on('notification', (event) => {
  console.log(event.message); // string
});
```


# Eventless (always) transitions (/docs/eventless-transitions)

**Eventless transitions** are transitions that happen without an explicit event. These transitions are *always* taken when the transition is enabled.

Eventless transitions are specified on the `always` state property and often referred to as “always” transitions.

<Callout>
  You can easily visualize and simulated eventless transitions in Stately’s editor. [Read more about eventless transitions in Stately’s editor](/docs/editor-states-and-transitions/#eventless-always-transitions).
</Callout>

```ts
import { createMachine } from 'xstate';
const machine = createMachine({
  states: {
    form: {
      initial: 'valid',
      states: {
        valid: {},
        invalid: {},
      },
      // [!code highlight:4]
      always: {
        guard: 'isValid',
        target: 'valid',
      },
    },
  },
});
```

## Eventless transitions and guards

Eventless transitions are taken immediately after normal transitions are taken. They are only taken if enabled, for example, if their [guards](guards) are true. This makes eventless transitions helpful in doing things when some condition is true.

## Avoid infinite loops

<Callout>
  Since unguarded “always” transitions always run, you should be careful not to create an infinite loop. XState will help guard against most infinite loop scenarios.
</Callout>

Eventless transitions with no `target` nor `guard` will cause an infinite loop. Transitions using `guard` and `actions` may run into an infinite loop if its `guard` keeps returning true.

You should define eventless transitions either with:

* `target`
* `guard` + `target`
* `guard` + `actions`
* `guard` + `target` + `actions`

If `target` is declared, the value should differ from the current state node.

## When to use

Eventless transitions can be helpful when a state change is necessary, but there is no specific trigger for that change.

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  id: 'kettle',
  initial: 'lukewarm',
  context: {
    temperature: 80,
  },
  states: {
    lukewarm: {
      on: {
        boil: { target: 'heating' },
      },
    },
    heating: {
      always: {
        guard: ({ context }) => context.temperature > 100,
        target: 'boiling',
      },
    },
    boiling: {
      entry: ['turnOffLight'],
      always: {
        guard: ({ context }) => context.temperature <= 100,
        target: 'heating',
      },
    },
  },
  on: {
    'temp.update': {
      actions: ['updateTemperature'],
    },
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=b2a299ef-efb8-4049-a242-2d197d27c931" title="Kettle with eventless transitions" />

## Eventless transitions and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

Eventless transitions can potentially be enabled by any event, so the `event` type is the union of all possible events.

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  types: {} as {
    events: { type: 'greet'; message: string } | { type: 'submit' };
  },
  // ...
  always: {
    actions: ({ event }) => {
      event.type; // 'greet' | 'submit'
    },
    guard: ({ event }) => {
      event.type; // 'greet' | 'submit'
      return true;
    },
  },
});
```

## Eventless transitions cheatsheet

### Cheatsheet: root eventless (always) transition

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  always: {
    guard: 'isValid',
    actions: ['doSomething'],
  },
  // ...
});
```

### Cheatsheet: state eventless (always) transition

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'start',
  states: {
    start: {
      always: {
        guard: 'isValid',
        target: 'otherState',
      },
    },
    otherState: {
      /* ... */
    },
  },
});
```


# XState examples (/docs/examples)

XState v5 examples are also available in the [`/examples` directory](https://github.com/statelyai/xstate/tree/main/examples). Many of the examples have a CodeSandbox link where you can run the example in your browser.

## Simple fetch example

A simple fetch example built with:

* XState v5

* Parcel

* [Simple fetch example on GitHub](https://github.com/statelyai/xstate/tree/main/examples/fetch)

* [Simple fetch example on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/fetch)

## 7GUIs counter (React)

An implementation of the [7GUIs counter](https://eugenkiss.github.io/7guis/tasks/#counter) built with:

* XState v5

* React

* TypeScript

* Vite

* [7GUIs counter (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/7guis-counter-react)

* [7GUIs counter (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/react-7guis-counter)

## 7GUIs temperature (React)

This is an implementation of the [7GUIs temperature converter](https://eugenkiss.github.io/7guis/tasks#temp) built with:

* XState v5

* React

* TypeScript

* Vite

* [7GUIs temperature (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/7guis-temperature-react)

* [7GUIs temperature (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/react-7guis-temperature)

## Simple list (React)

A React list built with:

* XState v5

* React

* TypeScript

* Vite

* [Simple list (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/friends-list-react)

* [Simple list (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/react-list)

## Stopwatch

A simple stopwatch built with:

* XState v5

* TypeScript

* Vite

* [Stopwatch on GitHub](https://github.com/statelyai/xstate/tree/main/examples/stopwatch)

* [Stopwatch on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/stopwatch)

## Tic-tac-toe game (React)

An implementation of tic-tac-toe built with:

* XState v5

* React

* TypeScript

* Vite

* [Tic-tac-toe game (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/tic-tac-toe-react)

* [Tic-tac-toe game (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/react-tic-tac-toe)

## Tiles game (React)

A simple tiles game built with:

* XState v5

* React

* TypeScript

* Vite

* [Tiles game (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/tiles)

* [Tiles game (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/tiles)

## TodoMVC (React)

An implementation of [TodoMVC](https://todomvc.com/) built with:

* XState v5

* React

* TypeScript

* Vite

* [TodoMVC (React) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/todomvc-react)

* [TodoMVC (React) on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/todomvc-react)

## Toggle

A simple toggle built with:

* XState v5

* TypeScript

* Vite

* [Toggle on GitHub](https://github.com/statelyai/xstate/tree/main/examples/toggle)

* [Toggle on CodeSandbox](https://codesandbox.io/p/sandbox/github/statelyai/xstate/tree/main/examples/toggle)

## Hello world workflow

Serverless hello world workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Hello-World-Example) built with:

* XState v5

[Hello world workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-hello)

## Greeting workflow

Serverless greeting workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Greeting-Example) built with:

* XState v5

[Greeting workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-greeting)

## Event-based greeting workflow

Serverless event-based greeting workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Event-Based-Greeting-Example) built with:

* XState v5

[Event-based greeting workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-event-greeting)

## Solving math problems

Serverless math solving problem workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Solving-Math-Problems-Example) built with:

* XState v5

[Solving math problems on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-math-problem)

## Parallel execution workflow

Serverless parallel execution workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Parallel-Execution-Example) built with:

* XState v5

[Parallel execution workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-parallel)

## Async function invocation workflow

Serverless async function invocation workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Async-Function-Invocation-Example) built with:

* XState v5

[Async function invocation workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-async-function)

## Async subflow invocation workflow

Serverless async subflow invocation workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Async-SubFlow-Invocation-Example) built with:

* XState v5

[Async subflow invocation workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-async-subflow)

## Event-based transitions (event-based switch) workflow

Serverless event-based transitions workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Event-Based-Transitions-Example) built with:

* XState v5

[Event-based transitions workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-event-based)

## Applicant request decision workflow

Serverless applicant request decision workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Applicant-Request-Decision-Example) built with:

* XState v5

[Applicant request decision workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-applicant-request)

## Provision orders (error handling) workflow

Serverless provision orders (error handling) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Provision-Orders-Example) built with:

* XState v5

[Provision orders (error handling) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-provision-orders)

## Monitor job for completion (polling) workflow

Serverless monitor job for completion (polling) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Monitor-Job-Example) built with:

* XState v5

[Monitor job for completion (polling) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-monitor-job)

## Send CloudEvent on workflow completion

Serverless send CloudEvent on workflow completion workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Send-CloudEvent-On-Workflow-Completion-Example) built with:

* XState v5

[Send CloudEvent on workflow completion on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-send-cloudevent)

## Monitor patient vital signs workflow

Serverless monitor patient vital signs workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Monitor-Patient-Vital-Signs-Example) built with:

* XState v5

[Monitor patient vital signs workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-monitor-patient)

## Finalize college application workflow

Serverless finalize college application workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Finalize-College-Application-Example) built with:

* XState v5

[Finalize college application workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-finalize-college-app)

## Perform customer credit check workflow

Serverless perform customer credit check workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Perform-Customer-Credit-Check-Example) built with:

* XState v5

[Perform customer credit check workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-credit-check)

## Handle car auction bids (scheduled start) workflow

Serverless handle car auction bids (scheduled start) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Handle-Car-Auction-Bids-Example) built with:

* XState v5

[Handle car auction bids (scheduled start) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-car-auction-bids)

## Check inbox periodically (cron-based workflow start)

Serverless check inbox periodically (cron-based workflow start) from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Check-Inbox-Periodically) built with:

* XState v5

[Check inbox periodically (cron-based workflow start) on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-check-inbox)

## Event-based service workflow

Serverless event-based service workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Event-Based-Service-Invocation) built with:

* XState v5

[Event-based service workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-event-based-service)

## Reusing function and event definitions workflow

Serverless reusing function and event definitions workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Reusing-Function-And-Event-Definitions) built with:

* XState v5

[Reusing function and event definitions workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-reusing-functions)

## New patient onboarding (error checking and retries) workflow

Serverless new patient onboarding (error checking and retries) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#new-patient-onboarding).

[New patient onboarding (error checking and retries) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-new-patient-onboarding)

## Purchase order deadline (ExecTimeout) workflow

Serverless purchase order deadline (ExecTimeout) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#purchase-order-deadline) built with:

* XState v5

[Purchase order deadline (ExecTimeout) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-purchase-order-deadline)

## Accumulate room readings and create timely reports (ExecTimeout and KeepActive) workflow

Serverless accumulate room readings and create timely reports (ExecTimeout and KeepActive) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#accumulate-room-readings) built with:

* XState v5

[Accumulate room readings and create timely reports (ExecTimeout and KeepActive) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-accumulate-room-readings)

## Car vitals checks (SubFlow Repeat) workflow

Store a single bid when the car auction is active.

Serverless car vitals checks (SubFlow Repeat) workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#handle-car-auction-bids-example) built with:

* XState v5

[Car vitals checks (SubFlow Repeat) workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-car-vitals)

## Book lending workflow

Serverless book lending workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#book-lending) built with:

* XState v5

[Book lending workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-book-lending)

## Filling a glass of water workflow

Serverless filling a glass of water workflow from the [CNCF Serverless Workflow examples](https://github.com/serverlessworkflow/specification/tree/main/examples#Filling-a-glass-of-water) built with:

* XState v5

[Filling a glass of water workflow on GitHub](https://github.com/statelyai/xstate/tree/main/examples/workflow-filling-water)

## More examples coming soon

If you have any examples you want us to make, please [add a request to our feedback board](https://feedback.stately.ai/examples) or upvote an existing suggestion.

If you have an example you want to share, [contribute your example to the XState repository](https://github.com/statelyai/xstate/tree/main/examples#contributing-an-example).


# Export as code (/docs/export-as-code)

import { Code, MoreHorizontal, Copy } from 'lucide-react';

Exporting as code is useful if you want to use your machine with [XState](xstate) inside your codebase or if you want to duplicate your machine without using **Fork**.

<Callout>
  You can now [generate a React app from your machine](generate-react).
</Callout>

Every feature of your state machine will be included in the code, except for [colors](colors) and [notes](annotations). Your last used export settings will be remembered next time you open the <Code size={18} /> **Code** panel.

## Export formats

You can export as code from <Code size={18} /> **Code** panel or the <MoreHorizontal size={18} /> menu alongside the machine name in the machines list.

Use the **XState version 5 beta** toggle to choose between code supported by XState version 4 and XState version 5. Copy the code to your clipboard with the <Copy size={18} /> copy button.

* JSON code for use with XState
* JavaScript code for use with XState
* TypeScript code for use with XState
* Markdown (*beta*) for use in documentation ([available on premium plans](studio-pro-plan))
* Stories (*beta*) for use in requirements and documentation ([available on premium plans](studio-pro-plan))
* [Mermaid](https://mermaid.js.org) code and diagrams for use in GitHub, GitLab, and anywhere Mermaid is supported

<Callout>
  You can also [import from code](import-from-code) from <Code size={18} /> **Code** panel or the <MoreHorizontal size={18} /> menu alongside the machine name in the machines list.
</Callout>

### Export to CodeSandbox and StackBlitz

You can export your machines to CodeSandbox and StackBlitz from the <Code size={18} /> **Code** panel. The machine will be exported in your chosen format as a basic JavaScript app that uses XState to run the machine.

<Callout>
  Wrap your Mermaid code in a fenced code block with the `mermaid` language identifier to [share your diagram on GitHub](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams#creating-mermaid-diagrams) and [share your diagram on GitLab](https://handbook.gitlab.com/handbook/tools-and-tips/mermaid/).
</Callout>


# Embed Figma (/docs/figma)

import { Plus, Paperclip, Star, Info, Settings } from 'lucide-react';

You can embed Figma frames in your states to keep your design and code in sync. Embedded Figma frames will stay up to date with the latest changes in your Figma files. Figma frames are a special type of [asset](assets).

[In Nick’s blog post, read about how you can improve your team workflows with Stately and Figma](../../blog/2024-01-24-embed-figma/).

<Callout>
  Embedding Figma frames is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=07a2b829-fc69-487c-af11-d6bb56c282a5" title="States with Figma assets" />

## How to embed Figma frames

1. [Generate a Figma personal access token and add it to your user Settings in Stately Studio](#figma-personal-access-token).
2. In Figma, right-click the frame you want to embed and select **Copy/Paste as** > **Copy link**.
3. Use the <Plus size={18} /> plus menu and <Paperclip size={18} /> **Embed Figma** on any selected state.
4. Paste the Figma link into the **Figma URL** field and click **Add Figma Asset**.

As Figma frames are a special type of [asset](assets), you can [change the display size of your Figma frame like any other asset](/docs/assets/#asset-sizes) and [change the order of your state’s assets](/docs/assets/#order-of-assets) to choose which asset is displayed on the state.

### Syncing Figma frames

Your embedded Figma frame will stay up to date with the latest changes in your Figma file unless:

* [you lock your machine](#locked-machines-with-figma-frames)
* [you revoke your Figma personal access token](#revoking-your-figma-personal-access-token)
* [your Figma personal access token expires](#revoking-your-figma-personal-access-token)

Refresh the page in the Stately Studio to see the latest changes from your embedded Figma frame.

## Locked machines with Figma frames

When your machine is [locked](lock-machines), updates to your Figma frames will not be synced to your machine. You can unlock your machine to sync your Figma frames again.

## Figma personal access token

You need a Figma [personal access token](https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens) to embed Figma frames. You can create a personal access token in [your Figma account settings](https://stately.ai/registry/user/my-settings?tab=Figma) under **Personal access tokens**.

To ensure Embed Figma works correctly, you must grant the following permissions to your personal access token:

* **File content**: Allow **Read-only** access so Stately Studio can sync your embedded Figma frames to your machines.

You can update your Figma personal access token from your user <Settings size={18} /> **Settings** in Stately Studio anytime.

### Revoking your Figma personal access token

If you need to revoke your Figma personal access token, you can do so from your Figma account settings.

When you revoke your Figma personal access token or your token expires, Stately Studio will no longer be able to sync your embedded Figma frames to your machines. The last synced version of your embedded Figma frames will remain in your machines for 14 days. After 14 days, Figma will entirely revoke access to the frame.


# Final states (/docs/final-states)

A final state is a state that represents the completion or successful termination of a machine. It is defined by the `type: 'final'` property on a state node:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      /* ... */
    },
    thanks: {
      /* ... */
    },
    // [!code highlight:3]
    closed: {
      type: 'final',
    },
    // ...
  },
  on: {
    'feedback.close': {
      target: '.closed',
    },
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=7d3feeca-1017-4d94-87b3-cd5128682440" title="Feedback machine with final state" />

When a machine reaches the final state, it can no longer receive any events, and anything running inside it is canceled and cleaned up. The box with a surrounding border icon represents the final state.

A machine can have multiple final states or no final states.

* A state machine can have zero or more final states. Some machines may run indefinitely and not need to terminate.
* Final states can have `output` data, which is sent to the parent machine when the machine terminates.
* When a machine reaches a top-level final state, it terminates.
* Final states cannot have transitions

## Top-level final states

A top-level final state is a final state that is a direct child state of the machine. When the machine reaches a top-level final state, the machine will terminate. When a machine terminates, it can no longer receive events nor transition.

## Child final states

When a child final state of a [parent (compound) state](./parent-states) is reached, that parent state is considered "done". The `onDone` transition of that parent state is automatically taken.

```ts
import { createMachine } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      initial: 'weighing',
      states: {
        weighing: {
          on: {
            weighed: {
              target: 'grinding',
            },
          },
        },
        grinding: {
          on: {
            ground: 'ready',
          },
        },
        // [!code highlight:4]
        ready: {
          // Child final state of parent state 'preparation'
          type: 'final',
        },
      },
      // [!code highlight:4]
      // Transition will be taken when child final state is reached
      onDone: {
        target: 'brewing',
      },
    },
    brewing: {
      // ...
    },
  },
});
```

## Final states in parallel states

When all regions of a parallel state are "done", the parallel state is considered "done". The `onDone` transition of the parallel state is taken.

In this example, the `preparation` state is a parallel state with two regions: `beans` and `water`. When both regions are done, the `preparation` state is done, and the `brewing` state is entered.

```ts
import { createMachine, createActor } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      type: 'parallel',
      states: {
        beans: {
          initial: 'grinding',
          states: {
            grinding: {
              on: {
                grindingComplete: 'ground',
              },
            },
            // [!code highlight:3]
            ground: {
              type: 'final',
            },
          },
        },
        water: {
          initial: 'heating',
          states: {
            heating: {
              always: {
                guard: 'waterBoiling',
                target: 'heated',
              },
            },
            // [!code highlight:3]
            heated: {
              type: 'final',
            },
          },
        },
      },
      // [!code highlight:1]
      onDone: 'brewing',
    },
    brewing: {},
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=95504ba2-3da2-4d70-a3b5-59bbcd31bf2d" title="Coffee machine with final states" />

## Output

When a machine reaches its top-level final state, it can produce output data. You can specify this output data in the `.output` property of the machine config:

```ts
import { createMachine, createActor } from 'xstate';

const currencyMachine = createMachine({
  // ...
  states: {
    converting: {
      // ...
    },
    converted: {
      type: 'final',
    },
  },
  // [!code highlight:4]
  output: ({ context }) => ({
    amount: context.amount,
    currency: context.currency,
  }),
});

const currencyActor = createActor(currencyMachine, {
  input: {
    amount: 10,
    fromCurrency: 'USD',
    toCurrency: 'EUR',
  },
});

currencyActor.subscribe({
  complete() {
    console.log(currencyActor.getSnapshot().output);
    // logs e.g. { amount: 12, currency: 'EUR' }
  },
});
```

The `.output` property can also be a static value:

```ts
import { createMachine, createActor } from 'xstate';

const processMachine = createMachine({
  // ...
  output: {
    message: 'Process completed.',
  },
});
```

## Final states cheatsheet

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      /* ... */
    },
    thanks: {
      /* ... */
    },
    // [!code highlight:3]
    closed: {
      type: 'final',
    },
    // ...
  },
  on: {
    'feedback.close': {
      target: '.closed',
    },
  },
});
```

## Cheatsheet: final states in parallel states

```ts
import { createMachine } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      type: 'parallel',
      states: {
        beans: {
          initial: 'grinding',
          states: {
            grinding: {
              on: {
                grindingComplete: 'ground',
              },
            },
            // [!code highlight:3]
            ground: {
              type: 'final',
            },
          },
        },
        water: {
          initial: 'heating',
          states: {
            heating: {
              always: {
                guard: 'waterBoiling',
                target: 'heated',
              },
            },
            // [!code highlight:3]
            heated: {
              type: 'final',
            },
          },
        },
      },
      // [!code highlight:1]
      onDone: 'brewing',
    },
    brewing: {},
  },
});
```


# Finite states (/docs/finite-states)

A finite state is one of the possible states that a state machine can be in at any given time. It's called "finite" because state machines have a known limited number of possible states. A state represents how a machine "behaves" when in that state; its status or mode.

For example in a feedback form, you can be in a state where you are filling out the form, or a state where the form is being submitted. You cannot be filling out the form and submitting it at the same time; this is an "impossible state."

State machines always start at an [initial state](initial-states), and may end at a [final state](final-states). The state machine is always in a finite state.

<Callout>
  You can easily visualize and simulate intial and final states in Stately's editor. [Read more about states in Stately's editor](/docs/editor-states-and-transitions).
</Callout>

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',

  // Initial state
  initial: 'prompt',

  // Finite states
  states: {
    prompt: {
      /* ... */
    },
    form: {
      /* ... */
    },
    thanks: {
      /* ... */
    },
    closed: {
      /* ... */
    },
  },
});
```

You can combine finite states with [context](context), which make up the overall state of a machine:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  context: {
    name: '',
    email: '',
    feedback: '',
  },

  initial: 'prompt',
  states: {
    prompt: {
      /* ... */
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

// Finite state
console.log(feedbackActor.getSnapshot().value);
// logs 'prompt'

// Context ("extended state")
console.log(feedbackActor.getSnapshot().context);
// logs { name: '', email: '', feedback: '' }
```

## Initial state

The initial state is the state that the machine starts in. It is defined by the `initial` property on the machine config:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',

  // [!code highlight:2]
  // Initial state
  initial: 'prompt',

  // Finite states
  states: {
    // [!code highlight:1]
    prompt: {
      /* ... */
    },
    // ...
  },
});
```

[Read more about initial states](initial-states).

## State nodes

In XState, a **state node** is a finite state "nodes" that comprise the entire statechart tree. State nodes are defined on the `states` property of other state nodes, including the root machine config (which itself is a state node):

```ts
import { createMachine } from 'xstate';

// The machine is the root state node
const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'prompt',

  // State nodes
  states: {
    // State node
    prompt: {
      /* ... */
    },
    // State node
    form: {
      /* ... */
    },
    // State node
    thanks: {
      /* ... */
    },
    // State node
    closed: {
      /* ... */
    },
  },
});
```

## Tags

State nodes can have **tags**, which are string terms that help group or categorize the state node. For example, you can signify which state nodes represent states in which data is being loaded by using a "loading" tag, and determine if a state contains those tagged state nodes with `state.hasTag(tag)`:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'prompt',
  states: {
    prompt: {
      tags: ['visible'],
      // ...
    },
    form: {
      tags: ['visible'],
      // ...
    },
    thanks: {
      tags: ['visible', 'confetti'],
      // ...
    },
    closed: {
      tags: ['hidden'],
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().hasTag('visible'));
// logs true
```

Read more about [tags](tags).

## Meta

Meta data is static data that describes relevant properties of a state node. You can specify meta data on the `.meta` property of any state node. This can be useful for displaying information about a state node in a UI, or for generating documentation.

The `state.meta` property collects the `.meta` data from all active state nodes and places them in an object with the state node's ID as the key and the `.meta` data as the value:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'prompt',
  meta: {
    title: 'Feedback',
  },
  states: {
    prompt: {
      meta: {
        content: 'How was your experience?',
      },
    },
    form: {
      meta: {
        content: 'Please fill out the form below.',
      },
    },
    thanks: {
      meta: {
        content: 'Thank you for your feedback!',
      },
    },
    closed: {},
  },
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().meta);
// logs the object:
// {
//   feedback: {
//     title: 'Feedback',
//   },
//   'feedback.prompt': {
//     content: 'How was your experience?',
//   }
// }
```

## Transitions

Transitions are how you move from one finite state to another. They are defined by the `on` property on a state node:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        'feedback.good': {
          target: 'thanks',
        },
      },
    },
    thanks: {
      /* ... */
    },
    // ...
  },
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().value);
// logs 'prompt'

feedbackActor.send({ type: 'feedback.good' });

console.log(feedbackActor.getSnapshot().value);
// logs 'thanks'
```

Read more about [events and transitions](transitions).

## Targets

A transition's `target` property defines where the machine should go when the transition is taken. Normally, it targets a sibling state node:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        'feedback.good': {
          // [!code highlight:2]
          // Targets the sibling `thanks` state node
          target: 'thanks',
        },
      },
    },
    thanks: {
      /* ... */
    },
    // ...
  },
});
```

The `target` can also target a descendant of a sibling state node:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        'feedback.good': {
          // [!code highlight:2]
          // Targets the sibling `thanks.happy` state node
          target: 'thanks.happy',
        },
      },
    },
    thanks: {
      initial: 'normal',
      states: {
        normal: {},
        // [!code highlight:1]
        happy: {},
      },
    },
    // ...
  },
});
```

When the target state node is a descendant of the source state node, the source state node key can be omitted:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    closed: {
      initial: 'normal',
      states: {
        normal: {},
        keypress: {},
      },
    },
  },
  on: {
    'feedback.close': {
      // [!code highlight:2]
      // Targets the descendant `closed` state node
      target: '.closed',
    },
    'key.escape': {
      // [!code highlight:2]
      // Targets the descendant `closed.keypress` state node
      target: '.closed.keypress',
    },
  },
});
```

When the state node doesn't change; i.e., the source and target state nodes are the same, the `target` property can be omitted:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    form: {
      on: {
        'feedback.update': {
          // [!code highlight:2]
          // No target defined – stay on the `form` state node
          // Equivalent to `target: '.form'` or `target: undefined`
          actions: 'updateForm',
        },
      },
    },
  },
});
```

State nodes can also be targeted by their `id` by prefixing the `target` with a `#` followed by the state node's `id`:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    // [!code highlight:3]
    closed: {
      id: 'finished',
    },
    // ...
  },
  on: {
    'feedback.close': {
      // [!code highlight:1]
      target: '#finished',
    },
  },
});
```

## Identifying state nodes

States can be identified with a unique ID: `id: 'myState'`. This is useful for targeting a state from any other state, even if they have different parent states:

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    // ...
    closed: {
      // [!code highlight:1]
      id: 'finished',
      type: 'final',
    },
    // ...
  },
  on: {
    'feedback.close': {
      // [!code highlight:2]
      // Target the `.closed` state by its ID
      target: '#finished',
    },
  },
});
```

State IDs do not affect the `state.value`. In the above example, the `state.value` would still be `closed` even though the state node is identified as `#finished`.

## Other state types

In statecharts, there are other types of states:

* [Parent states (also known as compound states)](parent-states)
* [Parallel states](parallel-states)
* [History states](history-states)
* [Final states](final-states)

## Modeling states

When designing finite states for your state machine, follow these guidelines to create maintainable and effective state machines:

### Start simple and shallow

* **Begin with minimal states**: Don't create multiple finite states until it becomes apparent that the behavior of your logic differs depending on some finite state it can be in.
* **Avoid premature optimization**: Start with basic states and add complexity only when necessary.
* **Prefer flat structures initially**: Deep nesting can be added later when patterns emerge.

### Identify distinct behaviors

* **Different behavior = different state**: Create separate states when the application behaves differently in response to the same event.
* **Same behavior = same state**: If multiple "states" handle events identically, they should probably be a single state.
* **Question impossible states**: Ask "can this combination of conditions exist?" If not, model them as separate states.

### Name states clearly

* **Use descriptive names**: State names should clearly describe what the machine is doing or what mode it's in.
* **Avoid technical jargon**: Use domain-specific language that stakeholders understand.
* **Be consistent**: Use consistent naming conventions across your state machines.

```ts
import { createMachine } from 'xstate';

// ❌ Poor naming
const machine = createMachine({
  initial: 'state1',
  states: {
    state1: {}, // What does this represent?
    state2: {}, // What does this represent?
    error: {}, // Too generic
  },
});

// ✅ Good naming
const authMachine = createMachine({
  initial: 'signedOut',
  states: {
    signedOut: {},
    signingIn: {},
    signedIn: {},
    authenticationFailed: {}, // Specific error state
  },
});
```

### Model user workflows

* **Follow the user journey**: States should reflect the natural progression of user actions.
* **Consider all paths**: Include happy paths, error states, and edge cases.
* **Account for loading states**: Async operations often need intermediate states.

```ts
import { createMachine } from 'xstate';

const checkoutMachine = createMachine({
  initial: 'cart',
  states: {
    cart: {
      on: {
        PROCEED: { target: 'shippingInfo' },
      },
    },
    shippingInfo: {
      on: {
        CONTINUE: { target: 'paymentInfo' },
        BACK: { target: 'cart' },
      },
    },
    paymentInfo: {
      on: {
        SUBMIT: { target: 'processing' },
        BACK: { target: 'shippingInfo' },
      },
    },
    processing: {
      on: {
        SUCCESS: { target: 'confirmed' },
        FAILURE: { target: 'paymentFailed' },
      },
    },
    paymentFailed: {
      on: {
        RETRY: { target: 'paymentInfo' },
      },
    },
    confirmed: {
      type: 'final',
    },
  },
});
```

### Consider temporal aspects

* **Time-sensitive states**: Model states that exist for specific durations.
* **Expiration handling**: Include states for handling timeouts and expirations.
* **Scheduled transitions**: Use delayed transitions for time-based state changes.

### Group related functionality

* **Use tags for categorization**: Group states by common characteristics.
* **Consider parent states**: When multiple states share common transitions, consider grouping them under a parent state.
* **Separate concerns**: Keep different domains or features in separate states.

```ts
import { createMachine } from 'xstate';

const appMachine = createMachine({
  initial: 'loading',
  states: {
    loading: {
      tags: ['busy'],
      on: {
        LOADED: { target: 'idle' },
        ERROR: { target: 'error' },
      },
    },
    idle: {
      tags: ['interactive'],
      on: {
        START_WORK: { target: 'working' },
      },
    },
    working: {
      tags: ['busy', 'interactive'],
      on: {
        COMPLETE: { target: 'idle' },
        CANCEL: { target: 'idle' },
      },
    },
    error: {
      tags: ['error'],
      on: {
        RETRY: { target: 'loading' },
      },
    },
  },
});
```

### Handle edge cases

* **Invalid states**: Model states for handling invalid or unexpected conditions.
* **Recovery states**: Provide ways to recover from error states.
* **Fallback behavior**: Include default states for unhandled scenarios.

### Validate state transitions

* **Ensure all transitions make sense**: Every state transition should represent a valid business logic change.
* **Avoid circular dependencies**: Be careful of states that can endlessly transition between each other without purpose.
* **Consider guards**: Use guards to prevent invalid transitions even when events are received.

### Document state purpose

* **Use descriptions**: Add `.description` properties to explain complex states.
* **Include meta data**: Store relevant information about what each state represents.
* **Comment complex logic**: Explain why certain states exist and what they accomplish.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      description: 'Waiting for user to indicate their satisfaction level',
      meta: {
        analytics: 'feedback_prompt_shown',
      },
    },
    collectingDetails: {
      description:
        'User provided negative feedback, collecting detailed information',
      meta: {
        analytics: 'detailed_feedback_form_shown',
      },
    },
  },
});
```

## Finite states and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the finite states of your machine using the `setup(...)` function, which provides excellent TypeScript inference and autocompletion:

```ts
import { setup, createActor } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as { feedback: string; rating: number },
    events: {} as
      | { type: 'feedback.good' }
      | { type: 'feedback.bad' }
      | { type: 'feedback.submit' },
  },
}).createMachine({
  id: 'feedback',
  initial: 'prompt',
  context: {
    feedback: '',
    rating: 0,
  },
  states: {
    prompt: {
      on: {
        'feedback.good': { target: 'thanks' },
        'feedback.bad': { target: 'form' },
      },
    },
    form: {
      on: {
        'feedback.submit': { target: 'thanks' },
      },
    },
    thanks: {
      type: 'final',
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

// ✅ Type-safe and autocompletes
const currentState = feedbackActor.getSnapshot();

// ✅ `state.matches(...)` is type-safe with autocompletion
if (currentState.matches('prompt')) {
  // TypeScript knows we're in the 'prompt' state
}

// ✅ All state values have autocompletion
const isFormState = currentState.matches('form');
const isThanksState = currentState.matches('thanks');

// ✅ `state.value` is also strongly typed
const stateValue = currentState.value; // 'prompt' | 'form' | 'thanks'
```

When using `setup(...).createMachine(...)`, TypeScript provides:

* **Type-safe state matching**: `state.matches(...)` with autocompletion for all possible state values
* **Strongly-typed state values**: `state.value` is typed as a union of all possible state names
* **Type-safe context**: Full type inference for `state.context`
* **Type-safe events**: `actor.send(...)` only accepts defined event types

## Finite states cheatsheet

### Cheatsheet: create finite states

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  id: 'feedback',
  initial: 'prompt',
  states: {
    prompt: {},
    form: {},
    thanks: {},
    closed: {},
  },
});
```

### Cheatsheet: finite states with transitions

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        'feedback.good': { target: 'thanks' },
        'feedback.bad': { target: 'form' },
      },
    },
    form: {
      on: {
        'feedback.submit': { target: 'thanks' },
      },
    },
    thanks: {},
  },
});
```

### Cheatsheet: read current state

```ts
import { createActor } from 'xstate';

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

// Read state value
console.log(state.value); // e.g., 'prompt'

// Check if in specific state
const isPromptState = state.matches('prompt');

// Check multiple states
const isFormOrThanks = state.matches('form') || state.matches('thanks');
```

### Cheatsheet: states with tags

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      tags: ['visible', 'interactive'],
    },
    form: {
      tags: ['visible', 'interactive'],
    },
    thanks: {
      tags: ['visible', 'success'],
    },
    closed: {
      tags: ['hidden'],
    },
  },
});

// Check tags
const state = actor.getSnapshot();
const isVisible = state.hasTag('visible');
const isInteractive = state.hasTag('interactive');
```

### Cheatsheet: states with meta data

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      meta: {
        title: 'How was your experience?',
        component: 'PromptView',
      },
    },
    form: {
      meta: {
        title: 'Tell us more',
        component: 'FormView',
      },
    },
  },
});

// Read meta data
const state = actor.getSnapshot();
console.log(state.getMeta());
```

### Cheatsheet: target states by ID

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'start',
  states: {
    start: {
      on: {
        FINISH: { target: '#completed' },
      },
    },
    process: {
      states: {
        step1: {},
        step2: {},
      },
    },
    done: {
      id: 'completed',
      type: 'final',
    },
  },
});
```

### Cheatsheet: strongly-typed finite states

```ts
import { setup } from 'xstate';

const machine = setup({
  types: {
    context: {} as { count: number },
    events: {} as { type: 'increment' } | { type: 'decrement' },
  },
}).createMachine({
  initial: 'idle',
  context: { count: 0 },
  states: {
    idle: {
      on: {
        increment: { target: 'active' },
      },
    },
    active: {
      on: {
        decrement: { target: 'idle' },
      },
    },
  },
});

// Type-safe state matching
const state = actor.getSnapshot();
const isIdle = state.matches('idle'); // ✅ Autocompletes
const stateValue = state.value; // ✅ 'idle' | 'active'
```


# Function Actors (/docs/function-actors)



# Generate with AI (/docs/generate-flow)

import { Sparkles, History } from 'lucide-react';

**Generate with AI** is an experimental feature that helps you auto-create machines from text descriptions. You can generate a flow for a new machine or use the flow description to describe how you want to modify your current flow.

<Callout>
  Community users can try generate flow with a limited number of generations each month. [Upgrade your existing plan](https://stately.ai/registry/billing) to get many more generations.
</Callout>

<Callout>
  **Generate with AI** is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

You have a limited number of generations available to use each month. The number of available generations is reset at the beginning of each calendar month.

There are many reasons you might want to generate your flows, including:

* You have a complex machine in mind and want to get started quickly
* You want an example of a state machine for a particular flow
* You’re new and want to get an idea of how you can model state machines

<YouTube id="wjZE39wCZXo" />

## Generate a new flow

Generating a new flow will overwrite your current machine with an all-new flow created from your text description.

1. Choose <Sparkles size={18} /> **Generate with AI** when creating a new machine or use the <Sparkles size={18} /> sparkles icon button in the canvas tools to open the generate flow dialog.
2. Enter a text description of the flow you want to generate in as much detail as possible, and use the **Generate** button to generate your flow.

When you generate an all-new flow, only your prompt text is sent to OpenAI.

## Generate from current flow

1. Use the <Sparkles size={18} /> sparkles icon button in the canvas tools to open the generate flow dialog.
2. Ensure **Generate from current flow** is selected to modify your current machine.
3. Enter a text description of the flow you want to generate in as much detail as possible, and use the **Generate** button to generate your flow.

When you use **Generate from current flow**, we share your machine definition with OpenAI to help build the response.

## View history

You can view a history of text descriptions used to generate flows for the current machine. Use the **View history** button inside the **Generate flow** dialog to find your **Prompt history**.

* Use **Open flow** to preview the [version](versions) of the machine generated from the selected prompt.
* Use **Copy to prompt** to copy the selected prompt into the text description area of the **Generate flow** dialog.

A new [version](versions) is auto-saved every time you generate a flow. Use the <History size={18} /> **Versions** panel and switch the **Show auto-saved versions** toggle on to browse auto-saved versions of your current machine.


# Generate React app (/docs/generate-react)

import { Code } from 'lucide-react';

**Generate React app** is an experimental feature that helps you generate a basic React app from your state machine.

In the <Code size={18} /> **Code** panel, use **Generate React app** to generate the files required to run a React app using your state machine. You can copy the code from the generated files, or use **Open Sandbox** to open your generated React app in CodeSandbox. You can also [use the **Enhance app** button](#enhance-app) to generate a React UI using Stately AI.

Generate React app generates code for use with [XState V5](xstate). [Read about how to migrate from XState V4 to V5](migration).

<Callout>
  Community users can try **Generate React app** with a limited number of generations each month. [Upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

<Callout>
  **Generate React app** is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

You have a limited number of generations available to use each month. The number of available generations is reset at the beginning of each calendar month.

## Enhance app

Use the **Enhance app** button to generate an enhanced user interface after generating your React app. Once generated, the dropdown menu in the top right of the modal gives you options to preview three different versions of your enhanced app, as well as the default app generated before the enhancement.

<Callout>
  When you use **Enhance app**, your state machine and generated React app are sent to OpenAI to generate the enhanced app.
</Callout>


# Generate test paths (/docs/generate-test-paths)

import { ClipboardCheck } from 'lucide-react';

You can generate test path code for your state machines from the <ClipboardCheck size={18} /> **Tests** panel, which you can use to implement tests in your code. Both setup code and test code for each path are generated.

This feature is in beta and [we’d love your feedback](https://feedback.stately.ai).

<Callout>
  **Generate test paths** is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

## Path generation strategy

You can choose between two path generation strategies:

* **Shortest path**: Generate paths that cover the shortest possible paths through your state machine and minimize the number of actions required to test functionality.
* **Simplest path**: Generate paths that cover the simple paths, without repeated states, through your state machine and help verify that basic functionality is working as expected.

## Testing frameworks

Generate test paths currently supports [Playwright](https://playwright.dev/) and custom testing framework formats. We plan to add more testing frameworks (including Puppeteer, Cypress, and even other languages) in the future.

## Options

* **Reduce duplicate paths** generates fewer paths that also cover other paths.
* **Prefer transition coverage** generates paths that cover all transitions in your state machine.
* **Highlight paths** enables highlighting the paths on the canvas when you hover over its test path description.
* **AI-generated test titles** (experimental) generates more readable test titles based on your state machine. AI-generated test titles send your state machine to OpenAI to generate the titles.


# Glossary (/docs/glossary)

This glossary is an alphabetical guide to the most common terms in statecharts and state machines.

<Callout>
  Looking for more detailed information on these concepts? [Read the introduction to state machines and statecharts](state-machines-and-statecharts).
</Callout>

## Actions

An [action](actions) is an effect that is executed during a state transition. Actions are “fire-and-forget effects”; once the machine has fired the action, it moves on and forgets the action.

## Actors

When you run a state machine, it becomes an [actor](actors), which is a running process that can receive events, send events, and change its behavior based on the events it receives, which can cause effects outside of the actor.

## After transitions

See [delayed transitions](#delayed-transitions).

## Always transitions

See [eventless transitions](#eventless-transitions).

## Compound states

See [parent and child states](#parent-and-child-states).

## Context

[Context](context) is the place that contextual data is stored in a state machine actor.

## Delayed transitions

[Delayed transitions](delayed-transitions) are transitions that only happen after a specified interval of time. If another event happens before the end of the timer, the transition doesn’t complete. Delayed transitions are labeled “after” and often referred to as “after” transitions.

## Eventless transitions

[Eventless transitions](eventless-transitions) are transitions without events. These transitions are *always* taken after any transition in their state is enabled. No event is necessary to trigger the transition. Eventless transitions are labeled “always” and often referred to as “always” transitions.

## Final state

When a machine reaches the [final state](final-states), it can no longer receive any events, and anything running inside it is canceled and cleaned up. A machine can have multiple final states or no final states.

## Guards

A [guard](guards) is a condition that the machine checks when it goes through an event. If the condition is true, the machine follows the transition to the next state. If the condition is false, the machine follows the rest of the conditions to the next state. Any transition can be a guarded transition.

## History state

A [history state](history-states) returns the parent state to its most recently active child state.

## Initial state

When a state machine starts, it enters the [**initial state**](initial-states) first. A machine can only have one top-level initial state.

## Invoked actors

An [invoked actor](actors) is an actor that can execute its own actions and communicate with the machine. These invoked actors are started in a state and stopped when the state is exited.

## Parallel states

A [parallel state](parallel-states) is a state separated into multiple regions of child states, where each region is active simultaneously.

## Parent and child states

States can contain more states, also known as [child states](parent-states). These child states are only active when the parent state is active. Child states are nested inside their parent states. Parent states are also known as compound states.

## States

A [state](states) describes the status of the machine. A state can be as simple as *active* and *inactive*. These states are finite; the machine can only move through the pre-defined states. A state machine can only be in one state at a time.

## Statecharts

[Statecharts](state-machines-and-statecharts) are a visual extension to state machines enabling you to model more complex logic, including hierarchy, concurrency, and communication.

## State machines

A [state machine](state-machines-and-statecharts) is a model that describes how the state of a process transitions to another state when an event occurs. State machines make building reliable software easier because they prevent impossible states and undesired transitions. When you run a state machine, it becomes an [actor](actors).

## Transitions and events

A machine moves from state to state through [transitions](transitions). Transitions are caused by events; when an event happens, the machine transitions to the next state. Transitions are “deterministic”; each combination of state and event always points to the same next state.


# Graph & Paths (/docs/graph)

<Callout>
  The graph utilities are now included in the main `xstate` package. Import from `xstate/graph` instead of the deprecated `@xstate/graph` package.
</Callout>

State machines can be represented as directed graphs, where states are nodes and transitions are edges. XState provides utilities to traverse these graphs and generate **paths**: sequences of events that transition a machine from one state to another.

## Why use path generation?

Path generation is useful for:

* **Model-based testing** - automatically generate test cases that cover all reachable states and transitions
* **Visualization** - understand the structure and flow of complex machines
* **Validation** - verify all states are reachable and all transitions are exercised
* **Documentation** - generate human-readable sequences of user flows

## Quick start

```ts
import { createMachine } from 'xstate';
import { getShortestPaths, getSimplePaths } from 'xstate/graph';

const machine = createMachine({
  initial: 'a',
  states: {
    a: {
      on: { NEXT: 'b', SKIP: 'c' }
    },
    b: {
      on: { NEXT: 'c' }
    },
    c: { type: 'final' }
  }
});

const shortestPaths = getShortestPaths(machine);
// - a
// - a -> b
// - a -> c (via SKIP, not through b)

const simplePaths = getSimplePaths(machine);
// - a
// - a -> b
// - a -> b -> c
// - a -> c (via SKIP)
```

## Core concepts

### Paths and steps

A **path** represents a sequence of transitions from one state to another. Each path contains:

* `state` - the final state reached by this path
* `steps` - array of steps taken to reach that state

A **step** represents a single transition:

* `state` - the state before this transition
* `event` - the event that triggered the transition

```ts
// Example path structure
{
  // The final state reached by this path
  state: { value: 'thanks', context: {} },
  // The steps taken to reach this state
  steps: [
    { state: { value: 'question' }, event: { type: 'CLICK_BAD' } },
    { state: { value: 'form' }, event: { type: 'SUBMIT' } }
  ]
}
```

### Shortest vs simple paths

**Shortest paths** use Dijkstra's algorithm to find the minimum number of transitions to reach each state. Use shortest paths when you want:

* One efficient path to each state
* Minimal test cases for state coverage
* Quick traversal verification

**Simple paths** use depth-first search to find all possible non-cyclic paths. Use simple paths when you want:

* Complete transition coverage
* All possible user flows
* Exhaustive testing

## `getShortestPaths(logic, options?)`

Returns the shortest path from the initial state to every reachable state.

```ts
import { createMachine } from 'xstate';
import { getShortestPaths } from 'xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: { target: 'thanks' },
        CLICK_BAD: { target: 'form' },
        CLOSE: { target: 'closed' }
      }
    },
    form: {
      on: {
        SUBMIT: { target: 'thanks' },
        CLOSE: { target: 'closed' }
      }
    },
    thanks: {
      on: {
        CLOSE: { target: 'closed' }
      }
    },
    closed: {
      type: 'final'
    }
  }
});

const paths = getShortestPaths(feedbackMachine);

// Returns array of paths:
// [
//   { state: 'question', steps: [] },
//   { state: 'thanks', steps: [{ state: 'question', event: { type: 'CLICK_GOOD' } }] },
//   { state: 'form', steps: [{ state: 'question', event: { type: 'CLICK_BAD' } }] },
//   { state: 'closed', steps: [{ state: 'question', event: { type: 'CLOSE' } }] }
// ]
```

Notice that reaching `closed` from `thanks` (2 steps) is not included because there's a shorter path directly from `question` (1 step).

## `getSimplePaths(logic, options?)`

Returns all simple (non-cyclic) paths from the initial state to every reachable state.

```ts
import { getSimplePaths } from 'xstate/graph';

const paths = getSimplePaths(feedbackMachine);

// Returns many more paths, including:
// - question → thanks (via CLICK_GOOD)
// - question → form → thanks (via CLICK_BAD, SUBMIT)
// - question → thanks → closed (via CLICK_GOOD, CLOSE)
// - question → form → thanks → closed (via CLICK_BAD, SUBMIT, CLOSE)
// - question → form → closed (via CLICK_BAD, CLOSE)
// - question → closed (via CLOSE)
// ... and more
```

Simple paths provide complete transition coverage - every valid sequence through the machine.

## `getPathsFromEvents(logic, events, options?)`

Traces a specific sequence of events and returns the resulting path. Useful for validating that a specific user flow works as expected.

```ts
import { getPathsFromEvents } from 'xstate/graph';

const path = getPathsFromEvents(feedbackMachine, [
  { type: 'CLICK_BAD' },
  { type: 'SUBMIT' },
  { type: 'CLOSE' }
]);

// Returns:
// {
//   state: { value: 'closed' },
// ,
//   steps: [
//     { state: { value: 'question' }, event: { type: 'CLICK_BAD' } },
//     { state: { value: 'form' }, event: { type: 'SUBMIT' } },
//     { state: { value: 'thanks' }, event: { type: 'CLOSE' } }
//   ]
// }
```

## Traversal options

All path functions accept an options object to customize traversal:

### `events`

Specify event payloads for events that require data. By default, events are traversed with just their type.

```ts
import { setup, assign } from 'xstate';
import { getShortestPaths } from 'xstate/graph';

const counterMachine = setup({
  types: {
    events: {} as { type: 'INC'; value: number }
  }
}).createMachine({
  id: 'counter',
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      on: {
        INC: {
          actions: assign({
            count: ({ context, event }) => context.count + event.value
          })
        }
      }
    }
  }
});

const paths = getShortestPaths(counterMachine, {
  events: [
    { type: 'INC', value: 1 },
    { type: 'INC', value: 5 },
    { type: 'INC', value: 10 }
  ]
});
```

You can also provide a function that returns events based on the current state:

```ts
const paths = getShortestPaths(counterMachine, {
  events: (state) => {
    // Generate different events based on context
    if (state.context.count < 10) {
      return [{ type: 'INC', value: 1 }];
    }
    return [{ type: 'INC', value: 10 }];
  }
});
```

### `toState`

Filter paths to only those reaching states matching a condition:

```ts
const paths = getShortestPaths(feedbackMachine, {
  toState: (state) => state.value === 'closed'
});

// Only returns paths ending in 'closed' state
```

### `fromState`

Start traversal from a specific state instead of the initial state:

```ts
import { createActor } from 'xstate';

const actor = createActor(feedbackMachine).start();
actor.send({ type: 'CLICK_BAD' });

const paths = getShortestPaths(feedbackMachine, {
  fromState: actor.getSnapshot()
});

// Paths starting from 'form' state
```

### `stopWhen`

Stop traversing when a condition is met:

```ts
const paths = getShortestPaths(counterMachine, {
  events: [{ type: 'INC', value: 1 }],
  stopWhen: (state) => state.context.count >= 5
});

// Stops exploring paths once count reaches 5
```

### `limit`

Maximum number of states to traverse (prevents infinite loops with context):

```ts
const paths = getShortestPaths(counterMachine, {
  events: [{ type: 'INC', value: 1 }],
  limit: 100 // Stop after 100 unique states
});
```

### `serializeState` and `serializeEvent`

Customize how states and events are serialized for comparison. By default, states are serialized as JSON strings of their value and context.

```ts
const paths = getShortestPaths(machine, {
  serializeState: (state) => {
    // Only consider state value, ignore context
    return JSON.stringify(state.value);
  },
  serializeEvent: (event) => {
    // Custom event serialization
    return event.type;
  }
});
```

## Working with context

When machines have dynamic context, the state space can become infinite. Use `stopWhen` or `limit` to bound the traversal:

```ts
import { setup, assign } from 'xstate';
import { getShortestPaths } from 'xstate/graph';

const counterMachine = setup({
  types: {
    events: {} as { type: 'INC'; value: number } | { type: 'DEC'; value: number }
  }
}).createMachine({
  id: 'counter',
  initial: 'counting',
  context: { count: 0 },
  states: {
    counting: {
      always: {
        target: 'done',
        guard: ({ context }) => context.count >= 10
      },
      on: {
        INC: {
          actions: assign({
            count: ({ context, event }) => context.count + event.value
          })
        },
        DEC: {
          actions: assign({
            count: ({ context, event }) => context.count - event.value
          })
        }
      }
    },
    done: {
      type: 'final'
    }
  }
});

const paths = getShortestPaths(counterMachine, {
  events: [
    { type: 'INC', value: 1 },
    { type: 'INC', value: 5 },
    { type: 'DEC', value: 1 }
  ],
  // Bound the state space
  stopWhen: (state) => state.context.count > 15 || state.context.count < -5
});
```

## `getAdjacencyMap(logic, options?)`

Returns a map representing the state machine as a graph, with states as keys and their transitions as values.

```ts
import { getAdjacencyMap } from 'xstate/graph';

const adjacencyMap = getAdjacencyMap(feedbackMachine);

// Structure:
// {
//   '"question"': {
//     state: { value: 'question', ... },
//     transitions: {
//       '{"type":"CLICK_GOOD"}': { event: {...}, state: {...} },
//       '{"type":"CLICK_BAD"}': { event: {...}, state: {...} },
//       '{"type":"CLOSE"}': { event: {...}, state: {...} }
//     }
//   },
//   '"form"': { ... },
//   ...
// }
```

## `toDirectedGraph(machine)`

Converts a machine to a directed graph structure for visualization:

```ts
import { toDirectedGraph } from 'xstate/graph';

const digraph = toDirectedGraph(feedbackMachine);

// Structure:
// {
//   id: 'feedback',
//   stateNode: StateNode,
//   children: [
//     { id: 'feedback.question', children: [], edges: [...] },
//     { id: 'feedback.form', children: [], edges: [...] },
//     ...
//   ],
//   edges: [
//     { source: StateNode, target: StateNode, transition: {...} },
//     ...
//   ]
// }
```

## Model-based testing

Path generation enables model-based testing - generating test cases directly from your state machine. Use `createTestModel` to wrap your machine with testing utilities:

```ts
import { createMachine } from 'xstate';
import { createTestModel } from 'xstate/graph';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' }
    },
    active: {
      on: { TOGGLE: 'inactive' }
    }
  }
});

const model = createTestModel(toggleMachine);

// Get paths for testing
const paths = model.getShortestPaths();

// Use with your test framework
describe('toggle', () => {
  for (const path of paths) {
    it(`reaches ${JSON.stringify(path.state.value)}`, async () => {
      await path.test({
        events: {
          TOGGLE: async () => {
            // Execute the toggle action in your app
            await page.click('#toggle-button');
          }
        },
        states: {
          inactive: async (state) => {
            // Assert the app is in inactive state
            await expect(page.locator('#status')).toHaveText('Inactive');
          },
          active: async (state) => {
            await expect(page.locator('#status')).toHaveText('Active');
          }
        }
      });
    });
  }
});
```

### TestModel methods

* `model.getShortestPaths(options?)` - get shortest paths
* `model.getSimplePaths(options?)` - get all simple paths
* `model.getPaths(pathGenerator)` - use custom path generator

### Path testing

Each path returned by `TestModel` has a `test` method that:

1. Starts from the initial state
2. Executes each event in the path using your event handlers
3. Verifies each state using your state assertions

```ts
path.test({
  events: {
    // Map event types to async functions that execute the event
    CLICK_GOOD: async () => await page.click('.good-button'),
    SUBMIT: async () => await page.click('button[type="submit"]')
  },
  states: {
    // Map state values to async assertions
    question: async () => await expect(page.locator('.question')).toBeVisible(),
    form: async () => await expect(page.locator('form')).toBeVisible(),
    thanks: async () => await expect(page.locator('.thanks')).toBeVisible()
  }
});
```

<Callout>
  You can [generate test paths from your state machines in Stately Studio](generate-test-paths.mdx), with support for Playwright, Vitest, and custom formats.
</Callout>

## Path deduplication

When using simple paths, you may get many paths where shorter paths are prefixes of longer ones. The `deduplicatePaths` utility removes redundant paths:

```ts
import { getSimplePaths, deduplicatePaths } from 'xstate/graph';

const allPaths = getSimplePaths(machine);
const uniquePaths = deduplicatePaths(allPaths);

// Removes paths that are prefixes of longer paths
// e.g., [A→B] is removed if [A→B→C] exists
```

## Example: Complete test generation

```ts
import { createMachine } from 'xstate';
import { createTestModel } from 'xstate/graph';
import { test, expect } from 'vitest';

const authMachine = createMachine({
  id: 'auth',
  initial: 'loggedOut',
  states: {
    loggedOut: {
      on: {
        LOGIN: 'loggingIn'
      }
    },
    loggingIn: {
      on: {
        SUCCESS: 'loggedIn',
        FAILURE: 'loggedOut'
      }
    },
    loggedIn: {
      on: {
        LOGOUT: 'loggedOut'
      }
    }
  }
});

const model = createTestModel(authMachine);

describe('auth flows', () => {
  const paths = model.getShortestPaths({
    toState: (state) => state.matches('loggedIn')
  });

  for (const path of paths) {
    test(path.description, async () => {
      // Setup
      const app = await setupApp();

      await path.test({
        events: {
          LOGIN: async () => {
            await app.fillLoginForm('user', 'pass');
            await app.submit();
          },
          SUCCESS: async () => {
            await app.mockAuthSuccess();
          },
          LOGOUT: async () => {
            await app.clickLogout();
          }
        },
        states: {
          loggedOut: async () => {
            expect(app.isLoggedIn()).toBe(false);
          },
          loggingIn: async () => {
            expect(app.isLoading()).toBe(true);
          },
          loggedIn: async () => {
            expect(app.isLoggedIn()).toBe(true);
          }
        }
      });
    });
  }
});
```


# Guards (/docs/guards)

A **guard** is a condition function that the machine checks when it goes through an event. If the condition is `true`, the machine follows the transition to the next state. If the condition is `false`, the machine follows the rest of the conditions to the next state.

A **guarded transition** is a transition that is enabled only if its `guard` evaluates to `true`. The guard determines whether or not the transition can be enabled. Any transition can be a guarded transition.

<Callout>
  You can easily visualize and simulate guarded transitions in Stately’s editor. [Read more about guards in Stately’s editor](/docs/editor-states-and-transitions/#add-guards).
</Callout>

Guards should be pure, synchronous functions that return either `true` or `false`.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine(
  {
    // ...
    states: {
      form: {
        on: {
          'feedback.submit': {
            // [!code highlight:1]
            guard: 'isValid',
            target: 'submitting',
          },
        },
      },
      submitting: {
        // ...
      },
    },
  },
  {
    // [!code highlight:5]
    guards: {
      isValid: ({ context }) => {
        return context.feedback.length > 0;
      },
    },
  },
);
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=2e91a914-7f83-45fe-9216-e1b5c495a44a" title="Feedback form with guards" />

## Multiple guarded transitions

If you want to have a single event transition to different states in certain situations, you can supply an array of guarded transitions. Each transition will be tested in order, and the first transition whose `guard` evaluates to `true` will be taken.

You can specify a default transition to be taken as the last transition in the array. If none of the guards evaluate to `true`, the default transition will be taken.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  prompt: {
    on: {
      // [!code highlight:15]
      'feedback.provide': [
        // Taken if 'sentimentGood' guard evaluates to `true`
        {
          guard: 'sentimentGood',
          target: 'thanks',
        },
        // Taken if none of the above guarded transitions are taken
        // and if 'sentimentBad' guard evaluates to `true`
        {
          guard: 'sentimentBad',
          target: 'form',
        },
        // Default transition
        { target: 'form' },
      ],
    },
  },
});
```

## Inline guards

You can define guards as an inline function. This is useful for quickly prototyping logic but we generally recommended using serialized guards (strings or objects) for better reusability and visualization.

```ts
on: {
  event: {
    guard: ({ context, event }) => true,
    target: 'someState'
  }
}
```

## Guard object

A guard can be defined as an object with a `type`, which is the type of guard that references the provided guard implementation, and optional `params`, which can be read by the implemented guard:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine(
  {
    // ...
    states: {
      // ...
      form: {
        on: {
          submit: {
            // [!code highlight:1]
            guard: { type: 'isValid', params: { maxLength: 50 } },
            target: 'submitting',
          },
        },
      },
      // ...
    },
  },
  {
    // [!code highlight:8]
    guards: {
      isValid: ({ context }, params) => {
        return (
          context.feedback.length > 0 &&
          context.feedback.length <= params.maxLength
        );
      },
    },
  },
);
```

Guards can later be provided or overridden by providing custom guard implementations in the `.provide()` method:

```ts
import { createActor } from 'xstate';

const feedbackActor = createActor(
  // [!code highlight:11]
  feedbackMachine.provide({
    guards: {
      isValid: ({ context }, params) => {
        return (
          context.feedback.length > 0 &&
          context.feedback.length <= params.maxLength &&
          isNotSpam(context.feedback)
        );
      },
    },
  }),
).start();
```

## Higher-level guards

XState provides higher-level guards, which are guards that compose other guards. There are three higher-level guards – `and`, `or`, and `not`:

* `and([...])` - evaluates to `true` if all guards in `and([...guards])` evaluate to `true`
* `or([...])` - evaluates to `true` if *any* guards in `or([...guards])` evaluate to `true`
* `not(...)` - evaluates to `true` if the guard in `not(guard)` evaluates to `false`

```ts
import { and } from 'xstate';

// ...

on: {
  event: {
    guard: and(['isValid', 'isAuthorized']);
  }
}
```

Higher-level guards can be combined:

```ts
import { and, or } from 'xstate';

// ...

on: {
  event: {
    guard: and(['isValid', or(['isAuthorized', 'isGuest'])]);
  }
}
```

## In-state guards

You can use the `stateIn(stateValue)` guard to check if the current state matches the provided `stateValue`. This is most useful for [parallel states](parallel-states).

```ts
import { stateIn } from 'xstate';

// ...

on: {
  event: {
    guard: stateIn('#state1');
  },
  anotherEvent: {
    guard: stateIn({ form: 'submitting' })
  }
}
```

In-state guards match the state of the entire machine, not the state node. There usually isn’t a need to use in-state guards for regular states. Try to model transitions in your state machines so that you don't need to use in-state guards first.

## Shorthands

It is recommended to define guards as guard objects, e.g. `{ type: 'someGuard', params: { ... } }`. However, if a guard has no params, you can specify it as a string:

```ts
on: {
  someEvent: {
    // Equivalent to:
    // guard: { type: 'someGuard' }
    // [!code highlight:1]
    guard: 'someGuard';
  }
}
```

## Guards and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `guards` of your machine by setting up their implementations in `setup({ guards: { … } })`. You can provide the `params` type in the 2nd argument of the guard function:

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:5]
  guards: {
    isGreaterThan: (_, params: { count: number; min: number }) => {
      return params.count > params.min;
    },
  },
}).createMachine({
  // ...
  on: {
    someEvent: {
      guard: {
        type: 'isGreaterThan',
        // Strongly-typed params
        params: ({ event }) => ({
          count: event.count,
          min: 10,
        }),
      },
      // ...
    },
  },
});
```

## Guards cheatsheet

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine(
  {
    // ...
    states: {
      form: {
        on: {
          'feedback.submit': {
            // [!code highlight:1]
            guard: 'isValid',
            target: 'submitting',
          },
        },
      },
      submitting: {
        // ...
      },
    },
  },
  {
    // [!code highlight:5]
    guards: {
      isValid: ({ context }) => {
        return context.feedback.length > 0;
      },
    },
  },
);
```

### Cheatsheet: multiple guarded transitions

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  prompt: {
    on: {
      // [!code highlight:15]
      'feedback.provide': [
        // Taken if 'sentimentGood' guard evaluates to `true`
        {
          guard: 'sentimentGood',
          target: 'thanks',
        },
        // Taken if none of the above guarded transitions are taken
        // and if 'sentimentBad' guard evaluates to `true`
        {
          guard: 'sentimentBad',
          target: 'form',
        },
        // Default transition
        { target: 'form' },
      ],
    },
  },
});
```

### Cheatsheet: Higher-level guards

```ts
import { createMachine, and } from 'xstate';

const loginMachine = createMachine({
  on: {
    event: {
      guard: and(['isValid', 'isAuthorized']);
    }
  }
});
```

### Cheatsheet: Combined higher-level guards

```ts
import { createMachine, and, or } from 'xstate';

const loginMachine = createMachine({
  on: {
    event: {
      guard: and(['isValid', or(['isAuthorized', 'isGuest'])]);
    }
  }
});
```


# History states (/docs/history-states)

A history state is a special type of state (a *pseudostate*) that remembers the last [child state](parent-states) that was active before its parent state is exited. When a transition from outside the parent state targets a history state, the remembered child state is entered.

This allows machines to "remember" where they left off when exiting and reentering a parent state.

* If no child state remembered, history goes to `.target` state, if it is specified
* Otherwise, go to [initial state](initial-states)

A history state returns the parent state to its most recently active child state. The box with an **H** inside represents the history state.

The history state can be deep or shallow:

* A shallow history state remembers the immediate child’s state.
* A deep history state remembers the deepest active state or states inside its child states.

```ts
import { createMachine } from 'xstate';

const checkoutMachine = createMachine({
  // ...
  states: {
    payment: {
      initial: 'card',
      states: {
        card: {},
        paypal: {},
        // [!code highlight:1]
        hist: { type: 'history' },
      },
    },
    address: {
      on: {
        back: {
          target: 'payment.hist',
        },
      },
    },
  },
});
```

## Shallow vs. deep history

* Shallow history states only remember the last active direct child state.
* Deep history states remember all active descendant states.

## History target

* Normally, history states target the most recent child state of its parent state
* If the history state is entered but the parent state was never visited, the parent's initial state is entered.
* However, you can add a `target: 'childKey'` to specify the default child state that should be entered

## History states cheatsheet

### Cheatsheet: create a history state (shallow by default)

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  // ...
  states: {
    hist: { type: 'history' },
    firstState: {},
    someState: {},
    anotherState: {},
  },
});
```

### Cheatsheet: create a deep history state

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  // ...
  states: {
    hist: {
      type: 'history',
      history: 'deep',
    },
    firstState: {},
    someState: {},
    anotherState: {},
  },
});
```

### Cheatsheet: create a history state with a target

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  // ...
  initialState: 'firstState',
  states: {
    hist: {
      type: 'history',
      target: 'someState',
    },
    firstState: {},
    someState: {},
    anotherState: {},
  },
});
```


# Share machine images using their image URL (/docs/image)

import { LinkIcon, MoreHorizontal } from 'lucide-react';

You can share an image of your machine anywhere that supports images. You can use the image URL for live-updating images where the machine is always updated with your latest changes. Machine images can be helpful in documentation, including GitHub pull requests.

The machine below demonstrates the copy image URL flow.

<EmbedMachine name="Copy image URL flow" embedURL="https://stately.ai/registry/editor/embed/1b050e43-c8a5-4e28-b881-71eadcc5b8a1" />

Your machine image will only be available if:

* the project visibility is **public** or **unlisted**

Machine images are not available for private machines.

Read [how to change a project’s visibility settings](projects.mdx#change-a-projects-visibility).

<Callout>
  You can also [embed your machine](embed) for a focused non-editable view of your machine in Stately Studio’s editor.
</Callout>

## Copy the image URL

Use the <LinkIcon size={16} /> **Copy image URL** option from the <MoreHorizontal size={18} /> triple dot icon alongside your machine name.

## Color mode

By default, the image’s color mode will be the same as your chosen Stately Studio color mode. Add `.light.png` or `.dark.png` to the URL to force that color mode.

## Examples

The examples below show how you can use the image URL.

### Markdown

```md
![State machine for the copy image URL flow in light mode.](https://stately.ai/registry/machines/1b050e43-c8a5-4e28-b881-71eadcc5b8a1.light.png)
```

### HTML

```html
<img
  src="https://stately.ai/registry/machines/1b050e43-c8a5-4e28-b881-71eadcc5b8a1.dark.png"
  alt="State machine for the copy image URL flow in dark mode."
/>
```


# Usage with Immer (/docs/immer)

[Immer](https://immerjs.github.io/immer/) is a library that makes it more convenient to work with updating data immutably. It can be used with XState to immutably update `context` in assignments.

<Callout>
  It is recommended to use Immer directly with XState instead of the `@xstate/immer` package, which is deprecated.
</Callout>

## Installation

Install the latest versions of `xstate` and `immer` from npm:

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate immer
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate immer
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate immer
    ```
  </Tab>
</Tabs>

See [the Immer installation docs](https://immerjs.github.io/immer/installation) for more information.

## Immer usage

XState already allows you to immutably update `context` partially or completely in [assign actions](/docs/actions#assign-action). However, for more complex scenarios, you may want to use Immer to update `context` in a less verbose way.

```ts
import { createMachine, assign } from 'xstate';
// [!code highlight:1]
import { produce } from 'immer';

const machine = createMachine({
  id: 'todos',
  context: {
    todos: [],
    filter: 'all',
  },
  // ...
  on: {
    'todo.complete': {
      // [!code highlight:8]
      // Using Immer to update a single context property
      actions: assign({
        todos: ({ context, event }) =>
          produce(context.todos, (draftTodos) => {
            const todo = draftTodos.find((t) => t.id === event.todo.id);
            todo.completed = true;
          }),
      }),
    },
    'todos.add': {
      // [!code highlight:14]
      // Using Immer to update multiple context properties
      actions: assign(({ context, event }) =>
        produce(context, (draftContext) => {
          draftContext.todos.push({
            id: event.todo.id,
            description: event.todo.description,
            completed: false,
          });

          if (draftContext.filter === 'all') {
            draftContext.filter = 'active';
          }
        }),
      ),
    },
  },
});
```


# Import from code (/docs/import-from-code)

import { Code, FilePlus2, Import } from 'lucide-react';

Importing from code is helpful if you’ve already built machines while working with [XState](xstate), or have created a machine using our older [Stately Viz](https://stately.ai/viz) but haven’t yet tried Stately Studio’s editor.

<Callout>
  Watch our [“Import from code” video on YouTube](https://www.youtube.com/watch?v=DAoIFaugDLo) (2m24s).
</Callout>

## Import state machines

Your state machine code should be formatted as a [`createMachine()` factory function](/docs/actors#createmachine) before import. The importer has basic validation in case your machine has basic errors, including reminding you if the `createMachine` definition is missing.

[Check out an importable machine code example at the end of this page](#machine-code-example).

<Callout type="warning">
  **Caution**: importing code will overwrite your current or selected machine unless you create a new machine from the machines list inside a project.
</Callout>

<Callout>
  The Stately editor now supports importing multiple machines from code.
</Callout>

### Create a new machine inside a project using imported code

Create a <FilePlus2 size={18} /> **New machine** from the machines list inside a project, then use the <Code size={18} /> **Import** button to import code into the new machine.

### Import code to overwrite your machine

Use **Import** button in the <Code size={18} /> **Code** panel, or **Machine** > <Import size={18} /> **Import** from the editor menu to overwrite your current machine.

## Machine code example

Below is an example of a `createMachine()` factory function which you can import as a machine without any errors:

```js
createMachine({
  id: 'Video',
  initial: 'Closed',
  description: 'Video player',
  states: {
    Closed: {
      on: {
        PLAY: {
          target: 'Opened',
        },
      },
    },
    Opened: {
      invoke: {
        src: 'startVideo',
      },
      initial: 'Playing',
      description: 'Fullscreen mode',
      states: {
        Playing: {
          on: {
            PAUSE: {
              target: 'Paused',
            },
          },
        },
        Paused: {
          on: {
            PLAY: {
              target: 'Playing',
            },
          },
        },
        Stopped: {
          type: 'final',
          after: {
            5000: {
              target: '#Video.Closed',
              actions: [],
              internal: false,
            },
          },
        },
      },
      on: {
        STOP: {
          target: '.Stopped',
        },
      },
    },
  },
  context: {},
  predictableActionArguments: true,
  preserveActionOrder: true,
});
```


# Connect GitHub repo (/docs/import-from-github)

import { Code, Github, Settings } from 'lucide-react';

You can connect a GitHub repo to a new project in Stately Studio, keeping updates between GitHub and Stately Studio in sync. Connecting a GitHub repo allows you to import your existing machines from GitHub and push changes to your machines back to your repo as pull requests.

<Callout>
  Connect GitHub repo is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

<Callout>
  While this feature is in beta, it works best when connecting less than 100
  files. For larger repos, we recommend creating single-file PRs. Read about
  that feature
  [here](../../blog/2024-02-16-changelog#make-github-pull-requests-for-single-files-from-inside-stately-studio).
</Callout>

## Connect GitHub repo

Use the **Connect GitHub repo** button found in the Projects dashboard to start connecting a GitHub repo. There are three steps to setting up your GitHub repo as a connected project:

1. Select the repo. You can choose the repos based on your GitHub permissions.
2. Select the branch. You can choose any branch in your repo.
3. Choose the files you want to sync. You can sync any JavaScript or TypeScript files, but only XState machines will be imported.

<Callout>
  Choose the XState version for your synced machines from the dropdown menu in the <Code size={18} /> **Code** panel.
</Callout>

### Auto-sync

Enabling **auto-sync** will automatically fetch the latest changes from your connected repository every time you open or refresh the project in the Studio.

Disable auto-sync from the GitHub options from the <Github size={18} /> repository name in the footer of the left drawer.

### Path for new files

You can choose a **Path for new files** for when you create new machines in this connected project. These machines will be added at this relative path from the root of your repo. The default path is **src/stately-studio** and will be created in your repo the first time you add new machines to a pull request.

### Create pull request

Open the GitHub options from the <Github size={18} /> repository name in the footer of the left drawer. You can create pull requests from the GitHub options for any changes made to your machine.

#### Update pull request

If you have already created a pull request for your machine, you can update the pull request with any changes you have made in the Studio. Open the GitHub options from the <Github size={18} /> repository name in the footer of the left drawer and choose **Update pull request**.

### Sync multiple branches

You can sync multiple branches in your GitHub repo to the same project in Stately Studio. Open the GitHub branch options from the branch name in the footer of the left drawer to choose additional branches.

### Sync new machines created in Stately Studio

Creating a new machine in Stately Studio in a connected project will flag the machine in your Machines list as **Not synced with GitHub**. You can sync the machine with your GitHub repo by [creating a pull request from the GitHub options](#create-pull-request) from the <Github size={18} /> repository name in the footer of the left drawer.

Your new machine will be added to your repo at the [**Path for new files**](#path-for-new-files) you have chosen.

### Sync changes to machines from GitHub

If you have [auto-sync enabled](#auto-sync), any changes made to your GitHub repo will be synced to your project in Stately Studio when you open or refresh the project in the Studio.

If you don’t have auto-sync enabled, you can sync any changes made to your GitHub repo using **Sync now** in the GitHub options from the <Github size={18} /> repository name in the footer of the left drawer.

## GitHub permissions

Importing your GitHub repos with **Connect GitHub repo** or importing a machine with a GitHub URL will prompt you to give our GitHub integration access to your GitHub repositories. You can choose from **Automatic setup** or **Personal access token**.

### Automatic setup

Automatic setup will enable access to all the repositories your GitHub user can access. You can [provide your own personal access token](#personal-access-token) if you need more granular control.

### GitHub personal access token

For more granular control, you can provide a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). You can create a personal access token in your GitHub account settings under [Developer Settings](https://github.com/settings/tokens).

To ensure GitHub Sync works correctly, you must grant the following permissions to your personal access token:

* **Contents**: Allow **Read and write** access so Stately Studio can import your machines and create files for machines added in the Studio.
* **Pull requests**: Allow **Read and write** access so Stately Studio can create pull requests for you (optional).

You can update your personal access token from your user <Settings size={18} /> **Settings** in Stately Studio anytime.

## Import machine from a GitHub URL

If you want to quickly import a machine, or multiple machines, from a GitHub file without syncing, you can import from a GitHub URL.

1. Open a file containing one or more machines on GitHub.
2. Modify the URL in the browser’s address bar to replace the `.com` with `.stately.ai`.
3. The editor will then display your imported machine.
4. **Save** the machine to enable editing and easily find your machine in your projects later.

Importing from a GitHub URL works with files in any branch in your private repositories, public repositories, and files in pull requests.

### Pull requests

Once you’ve made changes to your imported machine, you can use the **Create pull request** button to create a PR back to the source GitHub repository. If you want to make a pull request from your imported machine, you’ll need to provide a [GitHub personal access token](#github-personal-access-token).

### Bookmarklet

Use the bookmarklet below to import single file machines from GitHub with one click.

1. Add a new bookmark to your browser
2. Set the bookmark’s web address to the following:
   `javascript:(function(){ location.href = 'https://github.stately.ai/' + window.location.pathname;})();`
3. Click the bookmarklet when viewing a GitHub file containing one or more machines to import that machine to Stately Studio.

Or drag the following bookmarklet link to your bookmarks:

<a className="inline-block border-2 rounded-xl px-4 py-2" href="javascript:(function()%7Bjavascript%3A(function()%7B%20location.href%20%3D%20'https%3A%2F%2Fgithub.stately.ai%2F'%20%2B%20window.location.pathname%3B%7D)()%3B%7D)()%3B">
  GitHub → Stately
</a>

### Example

When your machine is hosted at GitHub:
`https://github.com/username/repo/blob/main/apps/superMachine.ts`, update the URL to
`https://github.stately.ai/username/repo/blob/main/apps/superMachine.ts` and Stately Studio will start the import.

<Callout>
  Read more in [our blog post about importing a machine from a GitHub URL](https://stately.ai/blog/2023-02-06-github-import-machines).
</Callout>


# Stately + XState docs (/docs)



import { LayoutIcon, BookOpenTextIcon, CodeIcon, RocketIcon } from 'lucide-react';
import { Cards, Card } from 'fumadocs-ui/components/card';

## Welcome to the Stately and XState docs

[Stately.ai](studio) is a visual software modeling platform for modeling your app & business logic as state machines/statecharts and actors, and scales to any level of complexity.

<div className="grid grid-cols-1 md:grid-cols-[400px_1fr] gap-4 items-start">
  <div id="xstate">
    <svg alt="XState" viewBox="0 0 818 222" xmlns="http://www.w3.org/2000/svg" className="w-full">
      <g fill="currentColor" fillRule="evenodd">
        <path d="M349.953 70.14c20.78 0 33.34 8.767 35.358 24.687l.061.48H373.49l-.074-.333c-2.34-10.754-9.356-15.342-23.463-15.342-12.799 0-20.752 5.323-20.752 13.889 0 8.456 3.477 12.681 24.072 15.378 21.535 2.94 34.883 8.334 34.883 26.096 0 15.089-15.264 26.467-35.5 26.467-29.518 0-37.608-15.437-39.196-28.39l-.059-.478h12.76l.054.363c1.47 9.876 5.691 19.016 26.438 19.016 13.178 0 22.74-6.83 22.74-16.239 0-10.026-5.659-13.879-24.218-16.485-17.819-2.376-34.307-7.016-34.307-24.738 0-14.122 13.914-24.371 33.085-24.371Zm122.576 1.11v11.316h-29.547v77.787H429.79V82.566h-29.547V71.25h72.285Zm52.908 0 34.709 89.103h-13.108l-9.947-25.427h-41.344l-9.8 25.427h-12.549l35.283-89.104h16.756Zm108.826 0v11.316h-29.547v77.787h-13.188V82.569H561.98V71.252h72.282v-.003Zm81.737 0v11.316h-49.311v27.44h25.027v11.195h-25.027v27.832H716v11.317h-62.5v-89.1H716ZM516.49 81.711l-16.154 42.994h32.306l-16.153-42.994ZM106.363 27l33.464.272c3.251.026 6.31 1.54 8.304 4.108l119.56 154.056c2.71 3.488.22 8.564-4.192 8.564h-35.035c-3.31 0-6.432-1.543-8.44-4.172l-33.345-45.635-36.905 45.777a10.623 10.623 0 0 1-8.33 4.03H106.32c-4.437 0-6.917-5.122-4.165-8.603l58.543-74.144-58.595-75.718c-2.68-3.51-.154-8.569 4.263-8.532Zm157.091 0c4.46-.034 6.973 5.108 4.207 8.603l-52.239 66.006c-3.726-2.932-32.644-27.14-4.745-59.556 0 0 2.183-2.554 4.435-5.195l.436-.511.435-.51a676.11 676.11 0 0 0 3.842-4.533c2.759-3.339 5.03-4.005 8.247-4.03L263.454 27Z" />
      </g>
    </svg>
  </div>

  <div>
    <a href="/docs/xstate">XState</a> is a best-in-class open source library for
    orchestrating and managing complex application logic in JavaScript and TypeScript apps.
  </div>
</div>

## Quick start

Install XState:

```bash
npm install xstate
```

Create a state machine:

```ts
import { createMachine, createActor } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { toggle: 'active' } },
    active: { on: { toggle: 'inactive' } },
  },
});

const toggleActor = createActor(toggleMachine);
toggleActor.start();

toggleActor.send({ type: 'toggle' });
console.log(toggleActor.getSnapshot().value); // 'active'

toggleActor.send({ type: 'toggle' });
console.log(toggleActor.getSnapshot().value); // 'inactive'
```

[Read the full quick start guide](/docs/quick-start) to learn more.

<Cards>
  <Card href="/docs/states" title={<><RocketIcon size="20" className="mr-2" /> Get started</>}>
    Jump straight into learning how to use Stately Studio editor, starting
    with states.
  </Card>

  <Card href="/docs/studio" title={<><LayoutIcon size="20" className="mr-2" /> Stately Studio overview</>}>
    Find out more about Stately Studio's visual editor and collaborating
    with your team using Stately Studio's premium features.
  </Card>

  <Card href="/docs/state-machines-and-statecharts" title={<><BookOpenTextIcon size="20" className="mr-2" /> Learn state machines and statecharts</>}>
    With our no-code introduction.
  </Card>

  <Card href="/docs/xstate" title={<><CodeIcon size="20" className="mr-2" /> Learn XState</>}>
    Get started with our JavaScript and TypeScript library for state
    machines and statecharts.
  </Card>
</Cards>

<EmbedMachine name="Default machine" embedURL="https://stately.ai/registry/editor/embed/5b170468-d66a-4136-84c8-676c8fea82e8?machineId=3e69d71f-1eda-41af-bee0-e82031d3f810" />

## Stately Studio or XState?

Stately Studio and XState are most powerful when used together. Use Stately Studio's visual editor to collaboratively model your app logic and use XState to integrate that logic into your codebase.

You can also use XState in your codebase without Stately Studio, and you're welcome to use Stately Studio if you're not yet familiar with XState.

## Who is Stately?

<img alt="The Stately team including Gavin, Farzad, David, Mateusz, Jenny, Laura, Anders, Nick, and Kevin, all standing in front of garage doors, laughing and smiling at each other." src={__img0} placeholder="blur" />

We're [Stately](https://stately.ai), a small team founded by [David Khourshid](https://twitter.com/davidkpiano), the creator of XState. Stately is building Stately Studio, where you can visualize your application logic and collaborate with your whole team.


# Initial states (/docs/initial-states)

When a state machine starts, it enters the **initial state** first. A machine can only have one top-level initial state; if there were multiple initial states, the machine wouldn’t know where to start!

In XState, the initial state is defined by the `initial` property on the machine config:

```ts
const feedbackMachine = createMachine({
  id: 'feedback',

  // [!code highlight:2]
  // Initial state
  initial: 'prompt',

  // Finite states
  states: {
    // [!code highlight:1]
    prompt: {
      /* ... */
    },
    // ...
  },
});
```

In our video player, paused is the initial state because the video player is paused by default and requires user interaction to start playing.

<Callout>
  Watch our [“What are initial states?” video on YouTube](https://www.youtube.com/watch?v=goCpmgyrjL0\&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ\&index=3) (1m17s).
</Callout>

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=3ebc8874-2294-480b-a06e-74845337cd8d" />

## Specifying an initial state

Typically, a state machine will have multiple [finite states](finite-states) that it can be in. The `initial` property on the machine config specifies the initial state that the machine should start in.

[Parent states](parent-states) also must specify an initial state in their `initial` property. The following `trafficLightMachine` will start in the `'green'` state, as it is specified in the `initial` property of the machine config.

When the machine reaches the `'red'` parent state, it will also be in the `'red.walk'` state, as it is specified in the `initial` property of the `'red'` state.

```ts
import { createMachine } from 'xstate';

const trafficLightMachine = createMachine({
  // [!code highlight:1]
  initial: 'green',
  states: {
    green: {
      /* ... */
    },
    yellow: {
      /* ... */
    },
    red: {
      // [!code highlight:1]
      initial: 'walk',
      states: {
        walk: {
          /* ... */
        },
        wait: {
          /* ... */
        },
        stop: {
          /* ... */
        },
      },
    },
  },
});

const trafficLightActor = createActor(trafficLightMachine);

trafficLightActor.subscribe((state) => {
  console.log(state.value);
});

trafficLightActor.start();
// logs 'green'
```


# Input (/docs/input)

Input refers to the data provided to a state machine that influences its behavior. In [XState](xstate), you provide input when creating an [actor](actors) using the second argument of the `createActor(machine, { input })` function:

```ts
import { createActor, setup } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as {
      userId: string;
      feedback: string;
      rating: number;
    },
    // [!code highlight:4]
    input: {} as {
      userId: string;
      defaultRating: number;
    },
  },
}).createMachine({
  // [!code highlight:1]
  context: ({ input }) => ({
    // [!code highlight:1]
    userId: input.userId,
    feedback: '',
    // [!code highlight:1]
    rating: input.defaultRating,
  }),
  // ...
});

const feedbackActor = createActor(feedbackMachine, {
  // [!code highlight:4]
  input: {
    userId: '123',
    defaultRating: 5,
  },
});
```

<Callout>
  Input is coming to Stately Studio’s editor soon.
</Callout>

## Creating actors with input

You can pass `input` to any kind of actor by reading this input from the `input` property of the first argument to actor logic creators, such as `fromPromise()`, `fromTransition()`, `fromObservable()`, and other actor logic creators.

**Input with `fromPromise()`:**

```ts
import { createActor, fromPromise } from 'xstate';

const userFetcher = fromPromise(({ input }: { input: { userId: string } }) => {
  return fetch(`/users/${input.userId}`).then((res) => res.json());
});

const userFetcherActor = createActor(userFetcher, {
  // [!code highlight:3]
  input: {
    userId: '123',
  },
}).start();

userFetcherActor.onDone((data) => {
  console.log(data);
  // logs the user data for userId 123
});
```

**Input with `fromTransition()`:**

```ts
import { createActor, fromTransition } from 'xstate';

const counter = fromTransition((state, event)) => {
  if (event.type === 'INCREMENT') {
    return { count: state.count + 1 };
  }
  return state;
}, ({ input }: { input: { startingCount?: number } }) => ({
  count: input.startingCount ?? 0,
});

const counterActor = createActor(counter, {
  // [!code highlight:23]
  input: {
    startingCount: 10,
  }
});
```

**Input with `fromObservable()`:**

```ts
import { createActor, fromObservable } from 'xstate';
import { interval } from 'rxjs';

const intervalLogic = fromObservable(
  ({ input }: { input: { interval: number } }) => {
    return interval(input.interval);
  },
);

const intervalActor = createActor(intervalLogic, {
  // highlight-start
  input: {
    interval: 1000,
  },
});

intervalActor.start();
```

## Initial event input

When an actor is started, it will automatically send a special event named `xstate.init` to itself. If `input` is provided to the `createActor(logic, { input })` function, it will be included in the `xstate.init` event:

```ts
import { createActor, createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // [!code highlight:4]
  entry: ({ event }) => {
    console.log(event.input);
    // logs { userId: '123', defaultRating: 5 }
  },
  // ...
});

const feedbackActor = createActor(feedbackMachine, {
  input: {
    userId: '123',
    defaultRating: 5,
  },
}).start();
```

## Invoking actors with input

You can provide input to invoked actors via the `input` property of the `invoke` configuration:

```ts
import { createActor, setup } from 'xstate';

const feedbackMachine = setup({
  actors: {
    liveFeedback: fromPromise(({ input }: { input: { domain: string } }) => {
      return fetch(`https://${input.domain}/feedback`).then((res) =>
        res.json(),
      );
    }),
  },
}).createMachine({
  invoke: {
    src: 'liveFeedback',
    // [!code highlight:3]
    input: {
      domain: 'stately.ai',
    },
  },
});
```

The `invoke.input` property can be a static input value or a function that returns the input value. The function will be called with an object that contains the current `context` and `event`:

```ts
import { createActor, setup } from 'xstate';

const feedbackMachine = setup({
  actors: {
    // [!code highlight:3]
    fetchUser: fromPromise(({ input }) => {
      return fetch(`/users/${input.userId}`).then((res) => res.json());
    }),
  },
}).createMachine({
  context: {
    userId: '',
    feedback: '',
    rating: 0,
  },
  invoke: {
    src: 'fetchUser',
    // [!code highlight:1]
    input: ({ context }) => ({ userId: context.userId }),
  },
  // ...
});
```

## Spawning actors with input

You can provide input to spawned actors via the `input` property of the `spawn` configuration:

```ts
import { createActor, setup, type AnyActorRef } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as {
      userId: string;
      feedback: string;
      rating: number;
      emailRef: AnyActorRef;
    },
  },
  actors: {
    // [!code highlight:6]
    emailUser: fromPromise(({ input }: { input: { userId: string } }) => {
      return fetch(`/users/${input.userId}`, {
        method: 'POST',
        // ...
      });
    }),
  },
}).createMachine({
  context: {
    userId: '',
    feedback: '',
    rating: 0,
    emailRef: null,
  },
  // ...
  on: {
    'feedback.submit': {
      actions: assign({
        emailRef: ({ context, spawn }) => {
          return spawn('emailUser', {
            // [!code highlight:1]
            input: { userId: context.userId },
          });
        },
      }),
    },
  },
  // ...
});
```

## Use-cases

Input is useful for creating reusable machines that can be configured with different input values.

* Replaces the old way of writing a factory function for machines:

```ts
// Old way: using a factory function
const createFeedbackMachine = (userId, defaultRating) => {
  return createMachine({
    context: {
      userId,
      feedback: '',
      rating: defaultRating,
    },
    // ...
  });
};

const feedbackMachine1 = createFeedbackMachine('123', 5);

const feedbackActor1 = createActor(feedbackMachine1).start();

// New way: using input
const feedbackMachine = createMachine({
  context: ({ input }) => ({
    userId: input.userId,
    feedback: '',
    rating: input.defaultRating,
  }),
  // ...
});

const feedbackActor = createActor(feedbackMachine, {
  input: {
    userId: '123',
    defaultRating: 5,
  },
});
```

### Passing new data to an actor

Changing the input will not cause the actor to be restarted. You need to send an event to the actor to pass the new data to the actor:

```tsx
const Component = (props) => {
  const feedbackActor = useActor(feedbackMachine, {
    input: {
      userId: props.userId,
      defaultRating: props.defaultRating,
    },
  });

  useEffect(() => {
    feedbackActor.send({
      type: 'userId.change',
      userId: props.userId,
    });
  }, [props.userId]);

  // ...
};
```

## Input and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `input` of your machine in the `types.input` property of the machine setup.

```ts
import { createActor, setup } from 'xstate';

const machine = setup({
  types: {
    // [!code highlight:4]
    input: {} as {
      userId: string;
      defaultRating: number;
    };
    context: {} as {
      userId: string;
      feedback: string;
      rating: number;
    };
  },
}).createMachine({
  context: ({ input }) => ({
    userId: input.userId,
    feedback: '',
    rating: input.defaultRating,
  }),
});

const actor = createActor(machine, {
  input: {
    userId: '123',
    defaultRating: 5,
  },
});
```

## Input cheatsheet

Use our XState input cheatsheet below to get started quickly.

### Cheatsheet: providing input

```ts
const feedbackActor = createActor(feedbackMachine, {
  input: {
    userId: '123',
    defaultRating: 5,
  },
});
```

### Cheatsheet: providing input to invoked actors

```ts
const feedbackMachine = createMachine({
  invoke: {
    src: 'liveFeedback',
    input: {
      domain: 'stately.ai',
    },
  },
});
```

### Cheatsheet: providing dynamic input to invoked actors

```ts
const feedbackMachine = createMachine({
  context: {
    userId: 'some-user-id',
  },
  invoke: {
    src: 'fetchUser',
    input: ({ context }) => ({ userId: context.userId }),
  },
});
```

### Cheatsheet: providing dynamic input from event properties to invoked actors

```ts
const feedbackMachine = createMachine({
  types: {
    events:
      | { type: 'messageSent', message: string }
      | { type: 'incremented', count: number },
  },
  invoke: {
    src: 'fetchUser',
    input: ({ event }) => {
      // [!code highlight:1]
      assertEvent(event, 'messageSent');
      return {
        message: event.message,
      };
    },
  },
});
```

### Cheatsheet: providing input to spawned actors

```ts
const feedbackMachine = createMachine({
  context: {
    userId: '',
  },
  // ...
  on: {
    'feedback.submit': {
      actions: assign({
        emailRef: ({ context, spawn }) => {
          return spawn('emailUser', {
            input: { userId: context.userId },
          });
        },
      }),
    },
  },
  // ...
});
```


# Inspection (/docs/inspection)

The Inspect API is a way to inspect the state transitions of your state machines and every aspect of actors in an actor system. Including:

* Actor lifecycle
* Actor event communication
* Actor snapshot updates
* State transition microsteps

<Callout>
  [We’ve recently released Stately Inspector](../../blog/2024-01-15-stately-inspector/), a universal tool that enables you to visually inspect the state of any application, frontend or backend, with the visualization of Stately’s editor.

  [Learn more about Stately Inspector](inspector)
</Callout>

The Inspect API lets you attach an “inspector,” an observer that observes inspection events, to the root of an actor system:

```tsx
const actor = createActor(machine, {
  inspect: (inspectionEvent) => {
    // type: '@xstate.actor' or
    // type: '@xstate.snapshot' or
    // type: '@xstate.event' or
    // type: '@xstate.microstep'
    console.log(inspectionEvent);
  },
});
```

The inspector will receive inspection events for every actor in the system, giving you granular visibility into everything happening, from how an individual actor is changing to how actors communicate with each other.

## Inspection events

Inspection events are event objects that have a `type` property that indicates the type of inspection event. There are four types of inspection events:

* `@xstate.actor` for [Actor inspection events](#actor-inspection-events)
* `@xstate.event` for [Event inspection events](#event-inspection-events)
* `@xstate.snapshot` for [Snapshot inspection events](#snapshot-inspection-events)
* `@xstate.microstep` for [Microstep inspection events](#microstep-inspection-events)

## Actor inspection events

The actor inspection event (`@xstate.actor`) is emitted when an actor in the system is created. It contains the following properties:

* `type` - the type of inspection event, always `'@xstate.actor'`
* `actorRef` - the reference to the actor
* `rootId` - the session ID of the root actor of the system

Example of an actor inspection event:

```js
{
  type: '@xstate.actor',
  actorRef: {/* Actor reference */},
  rootId: 'x:0',
}
```

## Event inspection events

The event inspection event (`@xstate.event`) is emitted when an event is sent to an actor. It contains the following properties:

* `type` - the type of inspection event, always `'@xstate.event'`
* `actorRef` - the reference to the target actor of the event
* `rootId` - the session ID of the root actor of the system
* `event` - the event object that was sent
* `sourceRef` - the reference to the source actor that sent the event, or `undefined` if the source is not known or an event was sent directly to the actor

Example of an event inspection event:

```js
{
  type: '@xstate.event',
  actorRef: {/* Actor reference */},
  rootId: 'x:0',
  event: {
    type: 'someEvent',
    message: 'hello'
  },
  sourceRef: {/* Actor reference */},
}
```

## Snapshot inspection events

The snapshot inspection event (`@xstate.snapshot`) is emitted when an actor's snapshot is updated. It contains the following properties:

* `type` - the type of inspection event, always `'@xstate.snapshot'`
* `actorRef` - the reference to the actor
* `rootId` - the session ID of the root actor of the system
* `snapshot` - the most recent snapshot of the actor
* `event` - the event that caused the snapshot to be updated

Example of a snapshot inspection event:

```js
{
  type: '@xstate.snapshot',
  actorRef: {/* Actor reference */},
  rootId: 'x:0',
  snapshot: {
    status: 'active',
    context: { count: 31 },
    // ...
  },
  event: {
    type: 'increment'
  }
}
```

## Microstep inspection events

The microstep inspection event (`@xstate.microstep`) is emitted for each individual state transition, including intermediate "microsteps", that occurs during the processing of an event. This is particularly useful for observing eventless transitions (like `always` transitions) and understanding the step-by-step progression through multiple states.

It contains the following properties:

* `type: '@xstate.microstep'`
* `value` - the current state value after this microstep
* `event` - the event that triggered this microstep
* `transitions` - an array of transition objects that occurred in this microstep

Each transition object in the `transitions` array contains:

* `eventType` - the event type that triggered the transition (empty string for eventless transitions)
* `target` - an array of target state paths

Example of a microstep inspection event:

```json5
{
  type: '@xstate.microstep',
  value: 'c',
  event: {
    type: 'EV',
  },
  transitions: [
    {
      eventType: 'EV',
      target: ['(machine).b'],
    },
    {
      eventType: '',
      target: ['(machine).c'],
    },
  ],
}
```

<details>
  <summary>
    Example of microstep events
  </summary>

  Here's an example of microstep events:

  ```tsx
  const machine = createMachine({
    initial: 'a',
    states: {
      a: {
        on: {
          EV: 'b',
        },
      },
      b: {
        always: 'c', // This will trigger automatically after entering state 'b'
      },
      c: {},
    },
  });

  const events = [];
  const actorRef = createActor(machine, {
    inspect: (ev) => events.push(ev),
  }).start();

  actorRef.send({ type: 'EV' });
  ```

  The microstep events will look like this:

  ```json5
  // First microstep: EV transition to state 'b'
  {
    type: '@xstate.microstep',
    value: 'b',
    event: {
      type: 'EV'
    },
    transitions: [
      {
        eventType: 'EV',
        target: ['(machine).b']
      }
    ]
  },

  // Second microstep: always transition to state 'c'
  {
    type: '@xstate.microstep',
    value: 'c',
    event: {
      type: 'EV'
    },
    transitions: [
      {
        eventType: '',  // Empty string indicates eventless transition
        target: ['(machine).c']
      }
    ]
  }
  ```
</details>


# Inspector (/docs/inspector)

# Stately Inspector

Stately Inspector is a tool that allows you to inspect your application’s state visually. It primarily works with frontend applications using XState but can also work with backend code and code that uses any state management solution.

<Callout>
  [Read about our recent release of Stately Inspector on our blog](../blog/2024-01-15-introducing-stately-inspector/).
</Callout>

## Install Stately Inspector

To inspect applications with Stately Inspector, install [Stately Inspect](https://github.com/statelyai/inspect) from npm via `@statelyai/inspect`:

```bash
npm install @statelyai/inspect
```

Then import the relevant inspector creator into your app. The creator is used to create an inspector (e.g., a browser or WebSocket inspector) that you can use to either connect to XState actors and/or manually send inspection events to Stately Inspector:

```ts
import { createActor } from 'xstate';
// [!code highlight:1]
import { createBrowserInspector } from '@statelyai/inspect';
import { machine } from './machine';

// [!code highlight:1]
const { inspect } = createBrowserInspector();

// ...

const actor = createActor(machine, {
  // [!code highlight:1]
  inspect,
  // ... other actor options
});

actor.start();
```

When you run your app, a new tab or popup window will open with the Inspector.

<Callout>
  When using the browser inspector, ensure that the popup window is not blocked by your browser’s popup blocker.
</Callout>

## Inspector options

You can pass the following options to the browser inspector:

* `filter` - a function that takes an inspection event and returns `true` if the event should be sent to the Stately Inspector.
* `serialize` - a function that takes an inspection event and allows you to serialize it before sending it to the Stately Inspector.
* `autoStart` - whether to automatically start the inspector. Defaults to `true`.
  * If `autoStart: false`, you can start the inspector by calling `inspector.start()`.
* `url` - the URL of the Stately Inspector to open. Defaults to `https://stately.ai/inspector`.
* `iframe` - the `<iframe>` element to use for the inspector. Defaults to `null`.

**Example usage:**

```ts
import { createBrowserInspector } from '@statelyai/inspect';

const inspector = createBrowserInspector({
  filter: (inspEvent) => {
    if (inspEvent.type === '@xstate.event') {
      // Skip mouse drag events
      return inspEvent.event.type !== 'mouse.drag';
    }
    return true;
  },
  iframe: document.getElementById('inspector-iframe'),
});
```

## Sending inspection events

The `@statelyai/inspect` package will send inspection events to the connected Stately Inspector. There are currently three kinds of events sent:

* Actor creation events
* Actor-to-actor communication events
* Actor snapshot changes

When you pass in the `inspect` option to the actor options in XState’s `createActor(machine, options)` function, it will automatically send all of these inspection events.

For usage with other state management solutions, you can manually send inspection events using the following methods:

* `inspector.actor(actor, snapshot, info)` - send actor creation events
* `inspector.event(actor, event, info)` - send actor-to-actor communication events
* `inspector.snapshot(actor, snapshot, info)` - send actor snapshot changes

```ts
import { createBrowserInspector } from '@statelyai/inspect';

const inspector = createBrowserInspector();

// Imagine a todo app...
inspector.actor('todos');

// When a todo is created
inspector.actor('todo-1', {
  context: { status: 'active' },
});

// When a user completes a todo
inspector.event('todo-1', { type: 'todo.complete' });

// When a todo changes
inspector.snapshot('todo-1', {
  context: { status: 'completed' },
});

// When the todos actor (not the user) sends an event to a todo
inspector.event(
  'todo-1',
  { type: 'todo.update' },
  {
    source: 'todos',
  },
);

// ... etc.
```

The three types of inspection events contain everything that Stately Inspector needs to generate two kinds of real-time diagrams automatically:

* **State machine diagrams** (if a state machine definition is provided)
* **Sequence diagrams**


# Installation (/docs/installation)

XState has **zero dependencies** and runs anywhere that JavaScript runs. You can install XState using your favorite package manager, like [npm](https://www.npmjs.com/), [pnpm](https://pnpm.js.org/), or [yarn](https://yarnpkg.com/).

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate
    ```
  </Tab>
</Tabs>

## CDN

You can also import XState from various CDNs:

* [esm.run](https://esm.run):

```ts
import { createMachine, createActor } from 'https://esm.run/xstate';
```

* [esm.sh](https://esm.sh):

```ts
import { createMachine, createActor } from 'https://esm.sh/xstate';
```

* [Skypack](https://www.skypack.dev):

```ts
import { createMachine, createActor } from 'https://cdn.skypack.dev/xstate';
```


# Invoke (/docs/invoke)

[State machines](machines) can “invoke” one or many [actors](actors) within a given state. The invoked actor will start when the state is entered, and stop when the state is exited. Any XState actor can be invoked, including simple Promise-based actors, or even complex machine-based actors.

Invoking an actor is useful for managing synchronous or asynchronous work that the state machine needs to orchestrate and communicate with at a high level, but doesn't need to know about in detail.

<Callout>
  [Read about the difference between invoking and spawning actors](actors.mdx#invoking-and-spawning-actors).
</Callout>

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=7f8f7dfb-f9a0-4e37-9c2a-bbca9f093d21&mode=design" title="User" />

<Callout>
  You can visualize your state machines and easily invoke actors in our drag-and-drop Stately editor. [Read more about actors in Stately’s editor](editor-actions-and-actors).
</Callout>

Actors can be invoked within any state *except* for the [top-level final state](final-states). In the following example, the `loading` state invokes a Promise-based actor:

```ts
import { setup, createActor, fromPromise, assign } from 'xstate';

const fetchUser = (userId: string) =>
  fetch(`https://example.com/${userId}`).then((response) => response.text());

const userMachine = setup({
  types: {
    context: {} as {
      userId: string;
      user: object | undefined;
      error: unknown;
    },
  },
  actors: {
    fetchUser: fromPromise(async ({ input }: { input: { userId: string } }) => {
      const user = await fetchUser(input.userId);

      return user;
    }),
  },
}).createMachine({
  id: 'user',
  initial: 'idle',
  context: {
    userId: '42',
    user: undefined,
    error: undefined,
  },
  states: {
    idle: {
      on: {
        FETCH: { target: 'loading' },
      },
    },
    loading: {
      // [!code highlight:13]
      invoke: {
        id: 'getUser',
        src: 'fetchUser',
        input: ({ context: { userId } }) => ({ userId }),
        onDone: {
          target: 'success',
          actions: assign({ user: ({ event }) => event.output }),
        },
        onError: {
          target: 'failure',
          actions: assign({ error: ({ event }) => event.error }),
        },
      },
    },
    success: {},
    failure: {
      on: {
        RETRY: { target: 'loading' },
      },
    },
  },
});
```

Actors can also be invoked on the *root* of the machine, and they will be active for the lifetime of their parent machine actor:

```ts
import { fromEvent } from 'rxjs';
import { fromEventObservable } from 'xstate';
const interactiveMachine = createMachine({
  // [!code highlight:5]
  invoke: {
    src: fromEventObservable(
      () => fromEvent(document.body, 'click') as Subscribable<EventObject>,
    ),
  },
  on: {
    click: {
      actions: ({ event }) => console.log(event),
    },
  },
});
```

And `invoke` can be an array, to invoke [multiple actors](#multiple-actors):

```ts
const vitalsWorkflow = createMachine({
  states: {
    CheckVitals: {
      // [!code highlight:6]
      invoke: [
        { src: 'checkTirePressure' },
        { src: 'checkOilPressure' },
        { src: 'checkCoolantLevel' },
        { src: 'checkBattery' },
      ],
    },
  },
});
```

For further examples, see:

* [Reusing function and event definitions workflow](https://github.com/statelyai/xstate/tree/main/examples/workflow-reusing-functions/main.ts)
* [Check inbox periodically (cron-based workflow)](https://github.com/statelyai/xstate/tree/main/examples/workflow-check-inbox/main.ts)
* [Car vitals checks (SubFlow Repeat) workflow](https://github.com/statelyai/xstate/tree/main/examples/workflow-car-vitals)

## How are actors different from actions?

Actions are “fire-and-forget”; as soon as their execution starts, the state machine running the actions forgets about them. If you specify an action as `async`, **the action won’t be awaited before moving to the next state**. Remember: transitions are always *zero-time* (states transition synchronously).

Invoked actors can do asynchronous work *and* communicate with their parent machine actor. They can send and receive events. Invoked machine actors can even invoke or spawn their own child actors.

Unlike actions, errors thrown by invoked actors can be handled directly:

```ts
invoke: {
  src: 'fetchUser',
  // [!code highlight:4]
  onError: {
    target: 'failure',
    actions: assign({ error: ({ event }) => event.error })
  }
}
```

Whereas errors thrown by actions can only be handled globally by a subscriber of their parent state machine:

```ts
actor.subscribe({
  error: (err) => {
    console.error(err);
  },
});
```

## Lifecycle

Invoked actors have a lifecycle that is managed by the state they are invoked in. They are created and started when the state is entered, and stopped when the state is exited.

If a state is entered and then immediately exited, e.g. due to an eventless ("always") transition, then no actors will be invoked on that state.

### Re-entering

By default, when a state machine transitions from a parent state to the same parent state or a descendent (child or deeper), it will *not* re-enter the parent state. Because the transition is not re-entering, the parent state's existing invoked actors will *not* be stopped and new invoked actors will *not* be started.

However, if you want a transition to re-enter the parent state, set the transition's `reenter` property to `true`. Transitions that re-enter the state *will* stop existing invoked actors and start new invoked actors.

[Read more about re-entering states](/docs/transitions#re-entering).

## The `invoke` property API

An invocation is defined in a state node's configuration with the `invoke` property, whose value is an object that contains:

* `src` - The source of the [actor logic](actors.mdx#actor-logic) to invoke when creating the actor, or a string referring to actor logic defined in the machine's [provided implementation](machines.mdx#providing-implementations).
* `id` - A string identifying the actor, unique within its parent machine.
* `input` - The input to pass to the actor.
* `onDone` - Transition that occurs when the actor is complete.
* `onError` - Transition that occurs when the actor throws an error.
* `onSnapshot` - Transition that occurs when the actor emits a new value.
* `systemId` - A string identifing the actor, unique system-wide.

### Source

The `src` represents the [actor logic](actors.mdx#actor-logic-creators) the machine should use when creating the actor. There are several actor logic creators available in XState:

* [State machine logic (`createMachine`)](/docs/actors/#createmachine)
* [Promise logic (`fromPromise`)](/docs/actors/#frompromise), where invoke will take the `onDone` transition on `resolve`, or the `onError` transition on `reject`
* [Transition function logic (`fromTransition`)](/docs/actors/#fromtransition), which follows the reducer pattern
* [Observable logic (`fromObservable`)](/docs/actors/#fromobservable), which can send events to the parent machine, and where invoke will take an `onDone` transition when completed
* [Event observable logic (`fromEventObservable`)](/docs/actors/#fromeventobservable), like Observable logic but for streams of event objects
* [Callback logic (`fromCallback`)](/docs/actors/#fromcallback), which can send events to and receive events from the parent machine

The invoke `src` can be *inline* or *provided*.

#### Inline `src`

Either directly inline:

```ts
invoke: {
  src: fromPromise(…)
}
```

Or from some logic in the same scope as the machine:

```ts
const logic = fromPromise(…)
const machine = createMachine({
  // …
  invoke: {
    src: logic
  }
});
```

#### Provided `src`

The `src` can be [provided in the machine implementation](machines.mdx#providing-implementations) and referenced using a string or an object.

```ts
const machine = createMachine({
  // …
  invoke: {
    src: 'workflow', // string reference
  },
});

const actor = createActor(
  machine.provide({
    actors: {
      workflow: fromPromise(/* ... */), // provided
    },
  }),
);
```

### `onDone`

* Transitions when invoked actor is complete
* Event object `output` property is provided with actor's output data
* Not available for callback actors

<Callout type="warning">
  Don't get the `onDone` property on a state confused with `invoke.onDone` - they are similar transitions, but refer to different things.

  * The `onDone` property on a [state](states) node refers to the compound state node reaching a [final state](final-states).
  * The `invoke.onDone` property refers to the invocation (`invoke.src`) being done.

  ```js
  // ...
  loading: {
    invoke: {
      src: someSrc,
      // refers to `someSrc` being done
      // [!code highlight:1]
      onDone: {/* ... */}
    },
    initial: 'loadFoo',
    states: {
      loadFoo: {/* ... */},
      loadBar: {/* ... */},
      loadingComplete: { type: 'final' }
    },
    // refers to 'loading.loadingComplete' being reached
    // [!code highlight:1]
    onDone: { target: 'loaded' }
  }
  // ...
  ```
</Callout>

The `onDone` transition can be an object:

```ts
{
  invoke: {
    src: 'fetchItem',
    // [!code highlight:6]
    onDone: {
      target: 'success',
      actions: ({ event }) => {
        console.log(event.output);
      }
    }
  }
}
```

Or, for simplicity, target-only transitions can be strings:

```ts
{
  invoke: {
    src: 'fetchItem',
    // [!code highlight:1]
    onDone: 'success',
  }
}
```

### `onError`

* Transitions when invoked actor throws an error, or (for Promise-based actors) when the promise rejects
* Event object `error` property is provided with actor’s error data

The `onError` transition can be an object:

```ts
invoke: {
  src: 'getUser',
  // [!code highlight:6]
  onError: {
    target: 'failure',
    actions: ({ event }) => {
      console.error(event.error);
    }
  }
}
```

Or, for simplicity, target-only transitions can be strings:

```ts
{
  invoke: {
    src: 'getUser',
    // [!code highlight:1]
    onError: 'failure'
  }
}
```

### `onSnapshot`

* Transitions when invoked actor emits a new snapshot
* Event gets `snapshot` with actor's snapshot
* Not available for callback actors

```ts
invoke: {
  src: 'getUser',
  // [!code highlight:3]
  onSnapshot: {
    actions: ({ event }) => console.log(event.snapshot)
  }
}
```

[Read more about actor snapshots](actors.mdx#actor-snapshots).

### Input

To define input to an invoked actor, use `input`.

The `input` property can be a static input value, or a function that returns the input value. The function will be passed an object that contains the current `context` and `event`.

<Callout>
  Behind the scenes, input is conveyed to the actor by an event:\
  `{ type: 'xstate.init', input: ... }`.
</Callout>

#### Input from a static value

```ts
invoke: {
  src: 'liveFeedback',
  // [!code highlight:3]
  input: {
    domain: 'stately.ai'
  }
}
```

#### Input from a function

```ts
invoke: {
  src: fromPromise(({ input: { endpoint, userId } }) => {
    return fetch(`${endpoint}/${userId}`).then((res) => res.json());
  }),
  // [!code highlight:4]
  input: ({ context, event }) => ({
    endpoint: context.endpoint,
    userId: event.userId
  })
}
```

See [Input](input) for more.

## Invoking Promises

The most common type of actors you’ll invoke are promise actors. Promise actors allow you to await the result of a promise before deciding what to do next.

XState can invoke Promises as actors using the `fromPromise` actor logic creator. Promises can:

* `resolve()`, which will take the `onDone` transition
* `reject()` (or throw an error), which will take the `onError` transition

If the state where the invoked promise is active is exited before the promise settles, the result of the promise is discarded.

```ts
import { setup, createActor, fromPromise, assign } from 'xstate';

// Function that returns a Promise
// which resolves with some useful value
// e.g.: { name: 'David', location: 'Florida' }
const fetchUser = (userId: string) =>
  fetch(`/api/users/${userId}`).then((response) => response.json());

const userMachine = setup({
  types: {
    context: {} as {
      userId: string;
      user: object | undefined;
      error: unknown;
    },
  },
}).createMachine({
  id: 'user',
  initial: 'idle',
  context: {
    userId: '42',
    user: undefined,
    error: undefined,
  },
  states: {
    idle: {
      on: {
        FETCH: { target: 'loading' },
      },
    },
    loading: {
      // [!code highlight:13]
      invoke: {
        id: 'getUser',
        src: fromPromise(({ input }) => fetchUser(input.userId)),
        input: ({ context: { userId } }) => ({ userId }),
        onDone: {
          target: 'success',
          actions: assign({ user: ({ event }) => event.output }),
        },
        onError: {
          target: 'failure',
          actions: assign({ error: ({ event }) => event.error }),
        },
      },
    },
    success: {},
    failure: {
      on: {
        RETRY: { target: 'loading' },
      },
    },
  },
});
```

The resolved output is placed into a `'xstate.done.actor.<id>'` event, under the `output` property, e.g.:

```js
{
  type: 'xstate.done.actor.getUser',
  output: {
    name: 'David',
    location: 'Florida'
  }
}
```

### Promise Rejection

If a Promise rejects, the `onError` transition will be taken with a `{ type: 'xstate.error.actor.<id>' }` event. The error data is available on the event's `error` property:

```ts
import { setup, createActor, fromPromise, assign } from 'xstate';

const search = (query: string) =>
  new Promise((resolve, reject) => {
    if (!query.length) {
      return reject('No query specified');
      // or:
      // throw new Error('No query specified');
    }

    return resolve(getSearchResults(query));
  });

// ...
const searchMachine = setup({
  types: {
    context: {} as {
      results: object | undefined;
      errorMessage: unknown;
    },
  },
}).createMachine({
  id: 'search',
  initial: 'idle',
  context: {
    results: undefined,
    errorMessage: undefined,
  },
  states: {
    idle: {
      on: {
        SEARCH: { target: 'searching' },
      },
    },
    searching: {
      // [!code highlight:19]
      invoke: {
        id: 'search',
        src: fromPromise(({ input: { query } }) => search(query)),
        input: ({ event }) => ({ query: event.query }),
        onError: {
          target: 'failure',
          actions: assign({
            errorMessage: ({ context, event }) => {
              // event is:
              // { type: 'xstate.error.actor.<id>', error: 'No query specified' }
              return event.error;
            },
          }),
        },
        onDone: {
          target: 'success',
          actions: assign({ results: ({ event }) => event.output }),
        },
      },
    },
    success: {},
    failure: {},
  },
});
```

If the `onError` transition is missing, and the Promise is rejected, the error will throw. However, you can handle all thrown errors for an actor by subscribing an observer object with an `error` function:

```ts
actor.subscribe({
  error: (err) => { ... }
})
```

## Invoking Callbacks

You can invoke [callback actor logic](./callback-actors) by:

1. Setting up the callback actor logic in the `actors` object of the `setup({ actors: { ... } })` call
2. Invoking the callback actor logic by its source name (`src`) in the `invoke` property of a state

```ts
import { setup, fromCallback } from 'xstate';

const machine = setup({
  actors: {
    // [!code highlight:3]
    someCallback: fromCallback(({ input, sendBack, receive }) => {
      // ...
    }),
  },
}).createMachine({
  // ...
  // [!code highlight:6]
  invoke: {
    src: 'someCallback',
    input: {
      /* ... */
    },
  },
});
```

Read [callback actor logic](./callback-actors) for more information on callback actors.

## Invoking Observables

You can invoke [observable logic](./observable-actors) by:

1. Setting up the observable logic in the `actors` object of the `setup({ actors: { ... } })` call
2. Invoking the observable logic by its source name (`src`) in the `invoke` property of a state

```ts
import { setup, fromObservable } from 'xstate';
import { interval } from 'rxjs';

const machine = setup({
  actors: {
    // [!code highlight:3]
    someObservable: fromObservable(({ input }: { input: number }) => {
      return interval(input.ms);
    }),
  },
}).createMachine({
  // ...
  // [!code highlight:9]
  invoke: {
    src: 'someObservable',
    input: { ms: 1000 },
    onSnapshot: {
      actions: ({ event }) => {
        console.log(event.snapshot.context); // 1, 2, 3, ...
      },
    },
  },
});
```

Read [observable actor logic](./observable-actors) for more information on observable actors.

## Invoking Event Observables

You can invoke [event observables](TODO) by using the `fromEventObservable(...)` actor logic creator. Event observable logic is similar to observable logic in that the parent actor subscribes to the event observable; however, the emitted values of an event observable are expected to be events that are *sent* to the invoking (parent) actor directly.

```ts
import { setup, fromEventObservable } from 'xstate';

const mouseClicks = fromEventObservable(/* ... */);

const machine = setup({
  actors: {
    mouseClicks,
  },
}).createMachine({
  // ...
  invoke: {
    src: 'mouseClicks',
    // No `onSnapshot` or `onDone` needed; events are sent directly to
    // the machine actor
  },
  on: {
    // Sent by the event observable actor
    click: {
      // ...
    },
  },
});
```

## Invoking Transitions

You can invoke [transition actor logic](./transition-actors) by:

1. Setting up the transition actor logic in the `actors` object of the `setup({ actors: { ... } })` call
2. Invoking the transition actor logic by its source name (`src`) in the `invoke` property of a state

```ts
import { setup, fromTransition } from 'xstate';

const machine = setup({
  actors: {
    // [!code highlight:4]
    someTransition: fromTransition((state, event, { input }) => {
      // ...
      return state;
    }),
  },
}).createMachine({
  // ...
  // [!code highlight:11]
  invoke: {
    src: 'someTransition',
    input: {
      /* ... */
    },
    onSnapshot: {
      actions: ({ event }) => {
        console.log(event.context);
      },
    },
  },
});
```

Read [transition actor logic](./transition-actors) for more information on transition actors.

## Invoking Machines

You can invoke [state machine actor logic](./state-machine-actors) by:

1. Setting up the state machine actor logic in the `actors` object of the `setup({ actors: { ... } })` call
2. Invoking the state machine actor logic by its source name (`src`) in the `invoke` property of a state

```ts
import { setup } from 'xstate';

const childMachine = setup({
  /* ... */
}).createMachine({
  context: ({ input }) => ({
    // ...
  }),
  // ...
});

const machine = setup({
  actors: {
    // [!code highlight:1]
    someMachine: childMachine,
  },
}).createMachine({
  // ...
  // [!code highlight:6]
  invoke: {
    src: 'someMachine',
    input: {
      /* ... */
    },
  },
});
```

Read [state machine actor logic](./state-machine-actors) for more information on state machine actors.

## Sending Responses

An invoked actor (or [spawned actor](./spawn)) can *respond* to another actor; i.e., it can send an event *in response to* an event sent by another actor. To do so, provide a reference to the sending actor as a custom property on the event object being sent. In the following example, we use `event.sender`, but any name works.

```js
// Parent
actions: sendTo('childActor', ({ self }) => ({
  type: 'ping',
  // [!code highlight:1]
  sender: self,
}));

// Child
actions: sendTo(
  // [!code highlight:1]
  ({ event }) => event.sender,
  { type: 'pong' },
);
```

In the following example, the `'client'` machine below sends the `'CODE'` event to the invoked `'auth-server'` actor, which then responds with a `'TOKEN'` event after 1 second.

```js
import { createActor, createMachine, sendTo } from 'xstate';

const authServerMachine = createMachine({
  id: 'server',
  initial: 'waitingForCode',
  states: {
    waitingForCode: {
      on: {
        CODE: {
          // [!code highlight:5]
          actions: sendTo(
            ({ event }) => event.sender,
            { type: 'TOKEN' },
            { delay: 1000 },
          ),
        },
      },
    },
  },
});

const authClientMachine = createMachine({
  id: 'client',
  initial: 'idle',
  states: {
    idle: {
      on: {
        AUTH: { target: 'authorizing' },
      },
    },
    authorizing: {
      invoke: {
        id: 'auth-server',
        src: authServerMachine,
      },
      // [!code highlight:4]
      entry: sendTo('auth-server', ({ self }) => ({
        type: 'CODE',
        sender: self,
      })),
      on: {
        TOKEN: { target: 'authorized' },
      },
    },
    authorized: {
      type: 'final',
    },
  },
});
```

Note that by default `sendTo` will send events anonymously, in which case the receiver will not know the source of the event.

<Callout>
  In XState v4, the `respond(...)` action creator was used for this purpose. In XState v5, use `sendTo(...)` instead.
</Callout>

## Multiple Actors

You can invoke multiple actors by specifying each in an array:

```ts
invoke: [
  { id: 'actor1', src: 'someActor' },
  { id: 'actor2', src: 'someActor' },
  { id: 'logActor', src: 'logActor' },
];
```

Each invocation will create a new instance of that actor, so even if the `src` of multiple actors are the same (e.g., `someActor` above), multiple instances of `someActor` will be invoked.

## Testing

You can test invoked actors by asserting that the parent actor receives expected events from the invoked actor.

```ts
const machine = setup({
  actors: {
    countLogic,
  },
}).createMachine({
  invoke: {
    src: 'countLogic',
  },
});
```

## Referencing Invoked Actors

Actors can be read on `snapshot.children.<actorId>`. The returned value is an `ActorRef` object, with properties like:

* `id` - the ID of the actor
* `send()`
* `getSnapshot()`

```ts
actor.subscribe({
  next(snapshot) {
    console.log(Object.keys(snapshot.children));
  },
});
```

`snapshot.children` is a key-value object where the keys are the actor ID and the value is the `ActorRef`.

## Invoke and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You should use the `setup({ ... })` API to properly infer types for invoked actor logic.

```ts
import { setup, fromPromise, assign } from 'xstate';

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

const machine = setup({
  actors: {
    // [!code highlight:5]
    fetchUser: fromPromise<User, { userId: string }>(async ({ input }) => {
      const response = await fetch(`https://example.com/${input.userId}`);

      return response.json();
    }),
  },
}).createMachine({
  // ...
  context: {
    user: null,
    userId: 42,
  },
  initial: 'idle',
  states: {
    idle: {
      on: {
        editUserDetails: { target: 'loadingUser' },
      },
    },
    loadingUser: {
      invoke: {
        // [!code highlight:9]
        src: 'fetchUser',
        input: ({ context }) => ({
          userId: context.userId, // Type enforced to be string
        }),
        onDone: {
          actions: assign({
            user: ({ event }) => event.output, // Strongly typed as User
          }),
        },
      },
    },
  },
});
```

Read the documentation on [setting up state machines](./setup) for more information.

## Invoke cheatsheet

### Cheatsheet: invoke an actor

```ts
import { setup, createActor, fromPromise, assign } from 'xstate';

const fetchUser = (userId: string) =>
  fetch(`https://example.com/${userId}`).then((response) => response.text());

const userMachine = setup({
  actors: {
    getUser: fromPromise(async ({ input }: { input: { userId: string } }) => {
      const data = await fetchUser(input.userId);

      return data;
    }),
  },
}).createMachine({
  // …
  states: {
    idle: {
      on: {
        FETCH: { target: 'loading' },
      },
    },
    loading: {
      // [!code highlight:13]
      invoke: {
        id: 'getUser',
        src: 'getUser',
        input: ({ context: { userId } }) => ({ userId }),
        onDone: {
          target: 'success',
          actions: assign({ user: ({ event }) => event.output }),
        },
        onError: {
          target: 'failure',
          actions: assign({ error: ({ event }) => event.error }),
        },
      },
    },
    success: {},
    failure: {
      on: {
        RETRY: { target: 'loading' },
      },
    },
  },
});
```

### Cheatsheet: invoke an actor on the root of the machine

```ts
import { createMachine } from 'xstate';
import { fromEventObservable, fromEvent } from 'rxjs';

const interactiveMachine = createMachine({
  // [!code highlight:5]
  invoke: {
    src: fromEventObservable(
      () => fromEvent(document.body, 'click') as Subscribable<EventObject>,
    ),
  },
  on: {
    click: {
      actions: ({ event }) => console.log(event),
    },
  },
});
```

### Cheatsheet: invoke multiple actors as an array

```ts
import { createMachine } from 'xstate';

const vitalsWorkflow = createMachine({
  states: {
    CheckVitals: {
      // [!code highlight:6]
      invoke: [
        { src: 'checkTirePressure' /* ... */ },
        { src: 'checkOilPressure' /* ... */ },
        { src: 'checkCoolantLevel' /* ... */ },
        { src: 'checkBattery' /* ... */ },
      ],
    },
  },
});
```


# Keyboard shortcuts (/docs/keyboard-shortcuts)

Stately Studio has a number of keyboard shortcuts.

<Tabs items={['macos', 'windows']}>
  <Tab value="macos" label="macOS">
    | Keyboard shortcut                                    | Description         |
    | ---------------------------------------------------- | ------------------- |
    | <kbd>Backspace</kbd>                                 | Delete              |
    | <kbd>Command</kbd> + <kbd>C</kbd>                    | Copy                |
    | <kbd>Command</kbd> + <kbd>V</kbd>                    | Paste               |
    | <kbd>Command</kbd> + <kbd>Z</kbd>                    | Undo                |
    | <kbd>Command</kbd> + <kbd>Shift</kbd> + <kbd>Z</kbd> | Redo                |
    | <kbd>Esc</kbd>                                       | Close panel or menu |
    | <kbd>+</kbd>                                         | Zoom in             |
    | <kbd>-</kbd>                                         | Zoom out            |
    | <kbd>Shift</kbd> + <kbd>0</kbd>                      | Zoom to 100%        |
    | <kbd>Shift</kbd> + <kbd>1</kbd>                      | Fit view to content |
    | <kbd>Arrow up</kbd>                                  | Pan up              |
    | <kbd>Shift</kbd> + <kbd>Arrow up</kbd>               | Big pan up          |
    | <kbd>Arrow down</kbd>                                | Pan down            |
    | <kbd>Shift</kbd> + <kbd>Arrow down</kbd>             | Big pan down        |
    | <kbd>Arrow left</kbd>                                | Pan left            |
    | <kbd>Shift</kbd> + <kbd>Arrow left</kbd>             | Big pan left        |
    | <kbd>Arrow right</kbd>                               | Pan right           |
    | <kbd>Shift</kbd> + <kbd>Arrow right</kbd>            | Big pan right       |
    | <kbd>Spacebar</kbd> + Mouse down                     | Drag to pan         |
    | <kbd>Command</kbd> + <kbd>.</kbd>                    | Show/hide UI        |
  </Tab>

  <Tab value="windows" label="Windows/Linux">
    | Keyboard shortcut                                 | Description         |
    | ------------------------------------------------- | ------------------- |
    | <kbd>Backspace</kbd>                              | Delete              |
    | <kbd>Ctrl</kbd> + <kbd>C</kbd>                    | Copy                |
    | <kbd>Ctrl</kbd> + <kbd>V</kbd>                    | Paste               |
    | <kbd>Ctrl</kbd> + <kbd>Z</kbd>                    | Undo                |
    | <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Z</kbd> | Redo                |
    | <kbd>Esc</kbd>                                    | Close panel or menu |
    | <kbd>+</kbd>                                      | Zoom in             |
    | <kbd>-</kbd>                                      | Zoom out            |
    | <kbd>Shift</kbd> + <kbd>0</kbd>                   | Zoom to 100%        |
    | <kbd>Shift</kbd> + <kbd>1</kbd>                   | Fit view to content |
    | <kbd>Arrow up</kbd>                               | Pan up              |
    | <kbd>Shift</kbd> + <kbd>Arrow up</kbd>            | Big pan up          |
    | <kbd>Arrow down</kbd>                             | Pan down            |
    | <kbd>Shift</kbd> + <kbd>Arrow down</kbd>          | Big pan down        |
    | <kbd>Arrow left</kbd>                             | Pan left            |
    | <kbd>Shift</kbd> + <kbd>Arrow left</kbd>          | Big pan left        |
    | <kbd>Arrow right</kbd>                            | Pan right           |
    | <kbd>Shift</kbd> + <kbd>Arrow right</kbd>         | Big pan right       |
    | <kbd>Spacebar</kbd> + Mouse down                  | Drag to pan         |
    | <kbd>Shift</kbd> + <kbd>.</kbd>                   | Show/hide UI        |
  </Tab>
</Tabs>

Do you have a keyboard shortcut you want to suggest? [Share a feature request](https://feedback.stately.ai).


# Live simulation mode (/docs/live-simulation)



import { Play, Triangle } from 'lucide-react';

Live simulation mode helps you share your simulated machine with your team without screen sharing or screenshots. Share the generated live simulation link, and your team can view your simulation in real-time and even trigger events themselves.

<Callout>
  Live simulation is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

{/* <p>
  <ThemedImage
    alt="Two views of the Studio, left is the user sharing the Live simulation and right is the user who joins the Live simulation."
    sources={{
      light: '/assets/live-sim/live-sim-side-by-side.png',
      dark: '/assets/live-sim/live-sim-side-by-side.png',
    }}
  />
  </p> */}

<img alt="Two views of the Studio, left is the user sharing the Live simulation and right is the user who joins the Live simulation." src={__img0} placeholder="blur" />

## Simulate your machine with others

You must be signed in to start a live simulation. Enter **Simulate** mode from the <Play size={18} /> **Simulate** button.

<Callout>
  When you’re in **Simulate** mode, your current state and next available events are highlighted in blue to simulate the flow of your machine. Click the available events to transition through states and review your logic.
</Callout>

Use the **Live mode** <Triangle size={18} /> button in the bottom left corner to reveal the **Live simulation** options and choose **Start a live simulation** to start a live simulation session.

When your machine is in live simulation mode, the **Live simulation panel** will display in the bottom left corner with a six-character unique code (for example: 3KNYII) along with **Copy link** and **Stop** buttons. The current state and next events will also be highlighted in orange.

Share your live simulation session with whoever you want using the link or unique session code.

## Join a Live simulation using a unique session code

1. Select the **Simulate** mode from the center of the visual editor.
2. Use the **Live mode** button in the bottom left corner to reveal the **Live simulation** options.
3. Choose **Join a Live simulation** to join an existing live simulation mode session.
4. Enter the unique session code you’ve been provided into the text box.
5. Select the **Join** button to join the session.

## Join a Live simulation using a unique session URL

Paste the URL shared with you into your browser address bar and hit enter to join the Live simulation session.


# Lock machines (/docs/lock-machines)

import { Lock, Unlock, Info } from 'lucide-react';

You can lock a machine anytime to prevent accidental edits. Locked machines have a <Lock size={18} /> **Locked** status displayed in the editor’s top bar and in the project’s machine list in the left panel.

Hover over the <Lock size={18} /> **Locked** text to view who locked the machine and when.

<Callout>
  Locking machines is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

Locking a machine prevents you, or anyone else on your team, from the following:

* Editing the machine
* Deleting the machine
* Renaming the machine
* [Importing code](import-from-code) to replace the machine
* [Restoring a past version](versions) of the machine

If somebody else is viewing or editing the machine when it is locked, their changes will not be saved, and they will be notified that the machine is now locked.

## How to lock and unlock a machine

From the editor menu, go to **Machine** > <Unlock size={18} /> **Lock machine** / <Lock size={18} /> **Unlock machine**.

From the machine’s <Info size={18} /> **Details** panel, use the <Unlock size={18} /> Lock machine / <Lock size={18} /> Unlock machine icon button.

If the machine is already locked, you can unlock it by clicking on the **Locked** button in the editor’s top bar and then confirming that you wish to unlock the machine.

## Team roles and locking machines

All team members can view locked machines. Only team owners, Admins, and Editors can lock and unlock machines.

| Capability              | Owner | Admin | Editor | Viewer |
| ----------------------- | ----- | ----- | ------ | ------ |
| View locked machine     | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| Lock and unlock machine | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |


# Machine restore (/docs/machine-restore)







The Studio is built using a server-client architecture.
Whenever you edit a machine, we will persist these changes to our database, ready for you to use when you return - on any device with an internet connection.

<img alt="Simplified drawing of the Stately Studio architecture described above." src={__img0} placeholder="blur" />

But what happens with your edits if your internet connection fails or the Stately backend is having problems?
This is where machine restore comes into play.

### Example

In our example, Santa's delivery machine has a problem; the Christmas-time state is unreachable.
So we do as any good Santa's helper would; we create an event to get to Christmas.
But alas, we're offline, so our changes could get lost.
Before this new feature, there was a chance you wouldn't notice you'd gone offline or that the server returned an error.
But as you can see in the GIF below, we've introduced new error banners to show you what's happening.

If you lose connectivity, we'll show the error banner saying, "You are offline, so we can't save your machine; we will try our best to store a local copy you can restore later."
So feel free to keep editing; you can restore your work the next time you visit this machine on the same device.

<img alt="Santa's delivery state machine shows an error while trying to edit offline." src={__img1} />

### How to restore

We try our best to detect any failure or missing connectivity when you edit your machines.
If you hit any error state, we will inform you and start saving your work locally on your device.
So next time you visit the Studio from that same device, we will compare what we saved with the machine from the server.
And if they are different, we will give you the option to restore the safe copy to a new machine.

When we detect that you have local changes on your device, we’ll present a modal titled “Restore offline machine?”.
Now you have two options:

1. **“No, delete the offline copy”** - This option will ignore, and delete, the changes found on your device and not show the modal again.
2. **“Yes, restore into a new machine”** - This option will create a new machine in your current project with the changes.

You can see this in action using our example from above.
When I return to the Studio, I can restore the changes I did while offline. Christmas is saved.

<img alt="Stately Studio shows an option to restore changes made to a machine while the user was offline." src={__img2} />

<Callout>
  Read more in [our blog post introducing machine restore](https://stately.ai/blog/2022-12-22-machine-recovery).
</Callout>


# State machines (/docs/machines)

A [state machine](state-machines-and-statecharts) is a model that describes the behavior of something, for example an [actor](actors). [Finite state](finite-states) machines describe how the state of an actor transitions to another state when an [event](transitions) occurs.

<Callout>
  Read our [introduction to state machines and statecharts](state-machines-and-statecharts) if you haven’t already!
</Callout>

## Benefits of state machines

State machines help build reliable and robust software. [Read more about the benefits of state machines](state-machines-and-statecharts.mdx#benefits-of-state-machines).

## Creating a state machine

In [XState](xstate), a state machine (referred to as a “machine”) is created using the `createMachine(config)` function:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        'feedback.good': {
          target: 'thanks',
        },
      },
    },
    thanks: {
      // ...
    },
    // ...
  },
});
```

In this example, the machine has two states: `question` and `thanks`. The `question` state has a transition to the `thanks` state when the `feedback.good` event is sent to the machine:

```ts
const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.value);
});

feedbackActor.start();
// logs 'question'

feedbackActor.send({ type: 'feedback.good' });
// logs 'thanks'
```

## Creating actors from machines

A machine contains the logic of an actor. An [actor](actors) is a running instance of the machine; in other words, it is the entity whose logic is described by the machine. Multiple actors can be created from the same machine, and each of those actors will exhibit the same behavior (reaction to received events), but they will be independent of each other and have their own states.

To create an actor, use the `createActor(machine)` function:

```ts
import { createActor } from 'xstate';

const feedbackActor = createActor(feedbackMachine);

feedbackActor.subscribe((state) => {
  console.log(state.value);
});

feedbackActor.start();
// logs 'question'
```

You can also create an actor [from other types of logic](/docs/actors/#actor-logic), such as [functions](/docs/actors/#fromtransition), [promises](/docs/actors/#frompromise), and [observables](/docs/actors/#fromobservable).

## Providing implementations

Machine implementations are the language-specific code that is executed but is not directly related to the state machine’s logic (states and transitions). This includes:

* [Actions](actions), which are fire-and-forget side-effects.
* [Actors](actors), which are entities that can communicate with the machine actor.
* [Guards](guards), which are conditions that determine whether a transition should be taken.
* [Delays](delayed-transitions), which specify the time before a delayed transition is taken or a delayed event is sent.

The default implementations can be provided in a `setup({...})` function when creating a machine, and then you can reference those implementations using JSON-serializable strings and/or objects, such as `{ type: 'doSomething' }`.

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  // Default implementations
  actions: {
    doSomething: () => {
      console.log('Doing something!');
    },
  },
  actors: {
    /* ... */
  },
  guards: {
    /* ... */
  },
  delays: {
    /* ... */
  },
}).createMachine({
  entry: { type: 'doSomething' },
  // ... rest of machine config
});

const feedbackActor = createActor(feedbackMachine);

feedbackActor.start();
// logs 'Doing something!'
```

You can override default implementations by *providing* implementations via `machine.provide(...)`. This function will create a new machine with the same config, but with the provided implementations:

```ts
const customFeedbackMachine = feedbackMachine.provide({
  actions: {
    doSomething: () => {
      console.log('Doing something else!');
    },
  },
});

const feedbackActor = createActor(customFeedbackMachine);

feedbackActor.start();
// logs 'Doing something else!'
```

## Type-bound action helpers

*Since XState version 5.22.0*

The `setup()` function provides type-bound action helpers that are fully typed to the setup's context, events, actors, guards, delays, and emitted types. These helpers create actions that are bound to the specific `setup()` they were created from and can be used directly in machines produced by that setup.

### Creating custom actions

Use `createAction(fn)` to create type-safe custom actions:

```ts
import { setup } from 'xstate';

const machineSetup = setup({
  types: {
    context: {} as { count: number; name: string },
    events: {} as { type: 'increment'; value: number } | { type: 'reset' },
  },
});

// Create a custom action with full type safety
// Can be defined in any file that imports the machineSetup
// [!code highlight:4]
const logCount = machineSetup.createAction(({ context, event }) => {
  // context and event are fully typed
  console.log(`Count: ${context.count}, Event: ${event.type}`);
});

const machine = machineSetup.createMachine({
  context: { count: 0, name: 'Counter' },
  initial: 'counting',
  states: {
    counting: {
      entry: logCount, // Fully typed action
      on: {
        increment: {
          actions: logCount,
        },
      },
    },
  },
});
```

The setup provides type-bound versions of all main built-in actions:

* `setup(…).assign(…)`
* `setup(…).raise(…)`
* `setup(…).emit(…)`
* `setup(…).sendTo(…)`
* `setup(…).log(…)`
* `setup(…).cancel(…)`
* `setup(…).spawnChild(…)`
* `setup(…).stopChild(…)`
* `setup(…).enqueueActions(…)`

These helpers are fully typed to your setup's types and don't require wrapper objects:

```ts
import { setup } from 'xstate';

const machineSetup = setup({
  types: {
    context: {} as { count: number; items: string[] },
    events: {} as { type: 'increment' } | { type: 'addItem'; item: string },
    emitted: {} as { type: 'COUNT_CHANGED'; count: number },
    // ...
  },
});

// [!code highlight:49]
// Type-bound assign - context is fully typed
const incrementCount = machineSetup.assign({
  count: ({ context }) => context.count + 1,
});

const addItem = machineSetup.assign({
  items: ({ context, event }) => [...context.items, event.item],
});

// Type-bound raise - events are fully typed
const raiseIncrement = machineSetup.raise({ type: 'increment' });

// Type-bound emit - emitted types are fully typed
const emitCountChanged = machineSetup.emit(({ context }) => ({
  type: 'COUNT_CHANGED',
  count: context.count,
}));

// Type-bound sendTo - actors are fully typed
const sendToLogger = machineSetup.sendTo('logger', ({ context }) => ({
  type: 'LOG',
  message: `Count is ${context.count}`,
}));

// Type-bound log - context and events are fully typed
const logContext = machineSetup.log(
  ({ context }) => `Context: ${JSON.stringify(context)}`,
);

// Type-bound cancel - actors are fully typed
const cancelLogger = machineSetup.cancel('logger');

// Type-bound stopChild - actors are fully typed
const stopLogger = machineSetup.stopChild('logger');

// Type-bound spawnChild - actors are fully typed
const spawnLogger = machineSetup.spawnChild('logger', {
  input: ({ context }) => ({ initialCount: context.count }),
});

// Type-bound enqueueActions - all helpers are available with full typing
const batchActions = machineSetup.enqueueActions(({ enqueue, check }) => {
  enqueue(incrementCount);
  enqueue(logContext);

  if (check(() => true)) {
    enqueue(emitCountChanged);
  }
});

const machine = machineSetup.createMachine({
  context: { count: 0, items: [] },
  initial: 'active',
  states: {
    active: {
      entry: [incrementCount, logContext, emitCountChanged],
      on: {
        increment: {
          actions: [incrementCount, batchActions],
        },
        addItem: {
          actions: addItem,
        },
      },
    },
  },
});
```

## Transitioning state

*Since XState version 5.19.0*

When you create a state machine actor, the next state is determined by the machine's current state and the event that is sent to the actor. However, you can also determine the next **state** and **actions** from the current state and event by using the pure `transition(machine, state, event)` and `initialTransition(machine)` functions:

```ts
// [!code highlight:1]
import { createMachine, initialTransition, transition } from 'xstate';

const machine = createMachine({
  initial: 'pending',
  states: {
    pending: {
      on: {
        start: { target: 'started' },
      },
    },
    started: {
      entry: 'doSomething',
    },
  },
});

// [!code highlight:1]
const [initialState, initialActions] = initialTransition(machine);

console.log(initialState.value);
// logs 'pending'

console.log(initialActions);
// logs []

// [!code highlight:1]
const [nextState, actions] = transition(machine, initialState, {
  type: 'start',
});

console.log(nextState.value);
// logs 'started'

console.log(actions);
// logs [{ type: 'doSomething', … }]
```

## Determining the next state

<Callout type="warning">
  It is recommended to use the `initialTransition(…)` and `transition(…)`
  functions instead of `getNextSnapshot(…)` and `getInitialSnapshot(…)`, which
  will be deprecated.
</Callout>

When you create a state machine actor, the next state is determined by the machine's current state and the event that is sent to the actor. If you want to determine the next state outside of the actor, you can use the `getNextSnapshot(…)` function:

```ts
import { getNextSnapshot } from 'xstate';
import { feedbackMachine } from './feedbackMachine';

const nextSnapshot = getNextSnapshot(
  feedbackMachine,
  feedbackMachine.resolveState({ value: 'question' }),
  { type: 'feedback.good' },
);

console.log(nextSnapshot.value);
// logs 'thanks'
```

You can also determine the initial state of a machine using the `getInitialSnapshot(…)` function:

```ts
import { getInitialSnapshot } from 'xstate';
import { feedbackMachine } from './feedbackMachine';

const initialSnapshot = getInitialSnapshot(
  feedbackMachine,
  // optional input
  { defaultRating: 3 },
);

console.log(initialSnapshot.value);
// logs 'question'
```

## Next transitions

*Since XState version 5.26.0*

You can get all potential next transitions from a given state using the `getNextTransitions(state)` function. This is useful for introspecting what events a state machine actor can respond to in its current state.

```ts
import { createMachine, createActor, getNextTransitions } from 'xstate';

const machine = createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        start: { target: 'running' },
        reset: { target: 'idle' },
      },
    },
    running: {
      on: {
        stop: { target: 'idle' },
        pause: { target: 'paused' },
      },
    },
    paused: {
      on: {
        resume: { target: 'running' },
        stop: { target: 'idle' },
      },
    },
  },
});

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

const transitions = getNextTransitions(actor.getSnapshot());

console.log(transitions.map((t) => t.eventType));
// logs ['start', 'reset']
```

The function returns an array of transition definitions, each of which is an object with the following properties:

* `eventType` - The event type of the transition
* `target` - The state node that the transition targets
* `source` - The state node where the transition originates from
* `actions` - The actions that will be executed during the transition
* `reenter` - Whether the transition is reentrant
* `guard` - The guard that will be evaluated to determine if the transition is enabled

This is particularly useful for:

* Building UIs that show available actions
* Debugging which transitions are possible
* Testing state machine behavior
* Generating documentation

## Specifying types

You can specify TypeScript types inside the machine setup using the `.types` property:

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as { feedback: string },
    events: {} as { type: 'feedback.good' } | { type: 'feedback.bad' },
  },
  actions: {
    logTelemetry: () => {
      // TODO: implement
    },
  },
}).createMachine({
  // ...
});
```

These types will be inferred throughout the machine config and in the created machine and actor so that methods such as `machine.transition(...)` and `actor.send(...)` will be type-safe.

## Modularizing states

*Since XState version 5.21.0*

You can use `.createStateConfig(...)` from the setup API to create modular, reusable state configurations. This approach provides several benefits, including modularity, strong typing, and better organization.

### Basic usage

```ts
import { setup } from 'xstate';

const lightMachineSetup = setup({
  // ...
});

// [!code highlight:25]
// Create individual state configurations
const green = lightMachineSetup.createStateConfig({
  entry: { type: 'startTimer' },
  on: {
    TIMER: { target: 'yellow' },
    PEDESTRIAN: { target: 'yellow' },
    EMERGENCY: { target: 'red' },
  },
});

const yellow = lightMachineSetup.createStateConfig({
  entry: { type: 'startTimer' },
  on: {
    TIMER: { target: 'red' },
    EMERGENCY: { target: 'red' },
  },
});

const red = lightMachineSetup.createStateConfig({
  entry: { type: 'startTimer' },
  on: {
    TIMER: { target: 'green' },
    EMERGENCY: { target: 'green' },
  },
});

// Compose the machine using the modular state configs
const trafficLightMachine = lightMachineSetup.createMachine({
  initial: 'green',
  states: {
    green,
    yellow,
    red,
  },
});
```

All state configurations created with `.createStateConfig(...)` have the full types specified in the setup config. The `.createStateConfig(...)` method is particularly useful for very large, complex state machines where you want to break down the logic into manageable pieces while maintaining strong typing throughout.

## Machines and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

The best way to provide strong typing for your machine is to use the `setup(...)` function and/or the `.types` property.

```ts
import { setup, fromPromise } from 'xstate';

const someAction = () => {
  /* ... */
};

const someGuard = ({ context }) => context.count <= 10;

const someActor = fromPromise(async () => {
  // ...
  return 42;
});

const feedbackMachine = setup({
  types: {
    context: {} as { count: number },
    events: {} as { type: 'increment' } | { type: 'decrement' },
  },
  actions: {
    someAction,
  },
  guards: {
    someGuard,
  },
  actors: {
    someActor,
  },
}).createMachine({
  initial: 'counting',
  states: {
    counting: {
      entry: { type: 'someAction' }, // strongly-typed
      invoke: {
        src: 'someActor', // strongly-typed
        onDone: {
          actions: ({ event }) => {
            event.output; // strongly-typed as number
          },
        },
      },
      on: {
        increment: {
          guard: { type: 'someGuard' }, // strongly-typed
          actions: assign({
            count: ({ context }) => context.count + 1,
          }),
        },
      },
    },
  },
});
```

## Machine cheatsheet

Use our XState machine cheatsheet below to get started quickly.

### Cheatsheet: create a machine

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'start',
  states: {
    start: {},
    // ...
  },
});
```

### Cheatsheet: setup a machine with implementations

```ts
import { setup } from 'xstate';

const machine = setup({
  actions: {
    someAction: () => {
      /* ... */
    },
  },
  guards: {
    someGuard: ({ context }) => context.count <= 10,
  },
  actors: {
    someActor: fromPromise(async () => {
      /* ... */
    }),
  },
  delays: {
    someDelay: () => 1000,
  },
}).createMachine({
  // ... Rest of machine config
});
```

### Cheatsheet: type-bound action helpers

```ts
import { setup } from 'xstate';

const machineSetup = setup({
  types: {
    context: {} as { count: number },
    events: {} as { type: 'increment' } | { type: 'reset' },
    emitted: {} as { type: 'COUNT_CHANGED' },
  },
});

// Custom action
const customAction = machineSetup.createAction(({ context, event }) => {
  console.log(context.count, event.type);
});

// Type-bound built-ins
const increment = machineSetup.assign({
  count: ({ context }) => context.count + 1,
});
const raiseReset = machineSetup.raise({ type: 'reset' });
const emitEvent = machineSetup.emit({ type: 'COUNT_CHANGED' });
const logCount = machineSetup.log(({ context }) => `Count: ${context.count}`);

const machine = machineSetup.createMachine({
  context: { count: 0 },
  entry: [customAction, increment, emitEvent],
  on: {
    increment: { actions: increment },
    reset: { actions: raiseReset },
  },
});
```

### Cheatsheet: provide implementations

```ts
import { createMachine } from 'xstate';
import { someMachine } from './someMachine';

const machineWithImpls = someMachine.provide({
  actions: {
    /* ... */
  },
  actors: {
    /* ... */
  },
  guards: {
    /* ... */
  },
  delays: {
    /* ... */
  },
});
```


# Migrating from XState v4 to v5 (/docs/migration)

The guide below explains how to migrate from XState version 4 to version 5. Migrating from XState v4 to v5 should be a straightforward process. If you get stuck or have any questions, please reach out to the Stately team on [our Discord](https://discord.gg/xstate).

<Callout>
  Read [David’s blog post on the launch of XState v5](../../blog/2023-12-01-xstate-v5).
</Callout>

This guide is for developers who want to update their codebase from v4 to v5 and should also be valuable for any developers wanting to know the differences between v4 and v5.

<Callout>
  Prefer video? [Watch our XState v5 webinar on YouTube](https://www.youtube.com/live/TRVjeil-y74).
</Callout>

## XState v5 and TypeScript

XState v5 and its related libraries are written in TypeScript, and utilize complex types to provide the best type safety and inference possible for you. **XState v5 requires TypeScript version 5.0 or greater.** For best results, use the latest TypeScript version.

Follow these guidelines to ensure that your TypeScript project is ready to use XState v5:

* Use the latest version of TypeScript, version 5.0 or greater (required)

  ```bash
  npm install typescript@latest --save-dev
  ```

* Set [`strictNullChecks`](https://www.typescriptlang.org/tsconfig#strictNullChecks) to `true` in your `tsconfig.json` file. This will ensure that our types work correctly and will also help catch errors in your code (strongly recommended)

  ```json5
  // tsconfig.json
  {
    compilerOptions: {
      // ...
      // [!code highlight:1]
      strictNullChecks: true,
      // or set `strict` to true, which includes `strictNullChecks`
      // "strict": true
    },
  }
  ```

* Set [`skipLibCheck`](https://www.typescriptlang.org/tsconfig#skipLibCheck) to `true` in your `tsconfig.json` file (recommended)

## Creating machines and actors

### Use `createMachine()`, not `Machine()`

<Callout type="warning">
  Breaking change
</Callout>

The `Machine(config)` function is now called `createMachine(config)`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts twoslash
    import { createMachine } from 'xstate';

    const machine = createMachine({
      // ...
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { Machine } from 'xstate';

    const machine = Machine({
      // ...
    });
    ```
  </Tab>
</Tabs>

### Use `createActor()`, not `interpret()`

<Callout type="warning">
  Breaking change
</Callout>

The `interpret()` function has been renamed to `createActor()`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    import { createMachine, createActor } from 'xstate';

    const machine = createMachine(/* ... */);

    // ✅
    const actor = createActor(machine, {
      // actor options
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    import { createMachine, interpret } from 'xstate';

    const machine = createMachine(/* ... */);

    // ❌ DEPRECATED
    const actor = interpret(machine, {
      // actor options
    });
    ```
  </Tab>
</Tabs>

### Use `machine.provide()`, not `machine.withConfig()`

<Callout type="warning">
  Breaking change
</Callout>

The `machine.withConfig()` method has been renamed to `machine.provide()`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const specificMachine = machine.provide({
      actions: {
        /* ... */
      },
      guards: {
        /* ... */
      },
      actors: {
        /* ... */
      },
      // ...
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const specificMachine = machine.withConfig({
      actions: {
        /* ... */
      },
      guards: {
        /* ... */
      },
      services: {
        /* ... */
      },
      // ...
    });
    ```
  </Tab>
</Tabs>

### Set context with `input`, not `machine.withContext()`

<Callout type="warning">
  Breaking change
</Callout>

The `machine.withContext(...)` method can no longer be used, as `context` can no longer be overridden directly. Use [input](input) instead:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      context: ({ input }) => ({
        actualMoney: Math.min(input.money, 42),
      }),
    });

    const actor = createActor(machine, {
      input: {
        money: 1000,
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      context: {
        actualMoney: 0,
      },
    });

    const moneyMachine = machine.withContext({
      actualMoney: 1000,
    });
    ```
  </Tab>
</Tabs>

### Actions ordered by default, `predictableActionArguments` no longer needed

<Callout type="warning">
  Breaking change
</Callout>

Actions are now in predictable order by default, so the `predictableActionArguments` flag is no longer required. Assign actions will always run in the order they are defined.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      entry: [
        ({ context }) => {
          console.log(context.count); // 0
        },
        assign({ count: 1 }),
        ({ context }) => {
          console.log(context.count); // 1
        },
        assign({ count: 2 }),
        ({ context }) => {
          console.log(context.count); // 2
        },
      ],
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      predictableActionArguments: true,
      entry: [
        (context) => {
          console.log(context.count); // 0
        },
        assign({ count: 1 }),
        (context) => {
          console.log(context.count); // 1
        },
        assign({ count: 2 }),
        (context) => {
          console.log(context.count); // 2
        },
      ],
    });
    ```
  </Tab>
</Tabs>

### The `spawn()` function has been removed

Instead of using the imported `spawn()` function to create actors inside `assign(...)` actions:

* Use the `spawnChild(...)` action creator (preferred)
* Or use the `spawn(...)` method from the first argument passed to the assigner function inside of `assign(...)` actions (useful if you need the actor ref in `context`)

Read the documentation on [spawning actors](./spawn) for more information.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    // [!code highlight:1]
    import { spawnChild, assign } from 'xstate';

    // Spawning a direct child:
    const machine1 = createMachine({
      // ...
      // [!code highlight:3]
      entry: spawnChild('someChildLogic', {
        id: 'someChild',
      }),
    });

    // Spawning a child with the actor ref in `context`:
    const machine2 = createMachine({
      // ...
      entry: assign({
        // [!code highlight:1]
        child: ({ spawn }) => spawn('someChildLogic'),
      }),
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌
    import { assign, spawn } from 'xstate';

    const machine = createMachine({
      // ...
      entry: assign({
        // [!code highlight:1]
        child: () => spawn('someChildLogic'),
      }),
    });
    ```
  </Tab>
</Tabs>

### Use `getNextSnapshot(…)` instead of `machine.transition(…)`

The `machine.transition(…)` method now requires an "actor scope" for the 3rd argument, which is internally created by `createActor(…)`. Instead, use `getNextSnapshot(…)` to get the next snapshot from some actor logic based on the current snapshot and event:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import {
      createMachine,
      // [!code highlight:1]
      getNextSnapshot,
    } from 'xstate';

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

    // [!code highlight:5]
    const nextState = getNextSnapshot(
      machine,
      machine.resolveState({ value: 'green' }),
      { type: 'timer' },
    );

    nextState.value; // yellow
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌
    import { createMachine } from 'xstate';

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

    const nextState = machine.transition('green', { type: 'timer' });

    nextState.value; // yellow
    ```
  </Tab>
</Tabs>

### Send events explictly instead of using `autoForward`

The `autoForward` property on invoke configs has been removed. Instead, send events explicitly.

In general, it's *not* recommended to forward all events to an actor. Instead, only forward the specific events that the actor needs.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      invoke: {
        src: 'someSource',
        id: 'someId',
      },
      // [!code highlight:5]
      always: {
        // Forward events to the invoked actor
        // This will not cause an infinite loop in XState v5
        actions: sendTo('someId', ({ event }) => event),
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌
    const machine = createMachine({
      // ...
      invoke: {
        src: 'someSource',
        id: 'someId'
        // [!code highlight:1]
        autoForward: true // deprecated
      }
    });
    ```
  </Tab>
</Tabs>

## States

### Use `state.getMeta()` instead of `state.meta`

<Callout type="warning">
  Breaking change
</Callout>

The `state.meta` property has been renamed to `state.getMeta()`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    state.getMeta();
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    state.meta;
    ```
  </Tab>
</Tabs>

### The `state.toStrings()` method has been removed

<Callout type="warning">
  Breaking change
</Callout>

```ts
import { type StateValue } from 'xstate';

export function getStateValueStrings(stateValue: StateValue): string[] {
  if (typeof stateValue === 'string') {
    return [stateValue];
  }
  const valueKeys = Object.keys(stateValue);

  return valueKeys.concat(
    ...valueKeys.map((key) =>
      getStateValueStrings(stateValue[key]!).map((s) => key + '.' + s),
    ),
  );
}

// ...

const stateValueStrings = getStateValueStrings(stateValue);
// e.g. ['green', 'yellow', 'red', 'red.walk', 'red.wait', …]
```

### Use `state._nodes` instead of `state.configuration`

<Callout type="warning">
  Breaking change
</Callout>

The `state.configuration` property has been renamed to `state._nodes`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    state._nodes;
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    state.configuration;
    ```
  </Tab>
</Tabs>

### Read events from inspection API instead of `state.events`

The `state.events` property has been removed, because events are not part of state, unless you explicitly add them to the state's `context`. Use the [inspection API](./inspection) to observe events instead, or add the event explicitly to the state's `context`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import { createActor } from 'xstate';
    import { someMachine } from './someMachine';

    const actor = createActor(someMachine, {
    // [!code highlight:5]
    inspect: (inspEvent) => {
    if (inspEvent.type === '@xstate.event') {
    console.log(inspEvent.event);
    }
    }
    });

    ```
  </Tab>

  <Tab value="v5-alt" label="XState v5 (context)">
    ```ts
    // ✅
    import { setup, createActor } from 'xstate';

    const someMachine = setup({
      // ...
      actions: {
        // [!code highlight:3]
        recordEvent: assign({
          event: ({ event }) => event
        })
      }
    }).createMachine({
      context: { event: undefined },
      on: {
        someEvent: {
          // ...
          // [!code highlight:1]
          actions: ['recordEvent']
        }
      }
    });

    const someActor = createActor(someMachine);
    someActor.subscribe(snapshot => {
      // [!code highlight:1]
      console.log(snapshot.context.event);
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { interpret } from 'xstate';
    import { someMachine } from './someMachine';

    const actor = interpret(someMachine);
    actor.subscribe((state) => {
      // [!code highlight:1]
      console.log(state.event); // Removed
    });
    ```
  </Tab>
</Tabs>

## Events and transitions

### Implementation functions receive a single argument

<Callout type="warning">
  Breaking change
</Callout>

Implementation functions now take in a single argument: an object with `context`, `event`, and other properties.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      entry: ({ context, event }) => {
        // ...
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      entry: (context, event) => {
        // ...
      },
    });
    ```
  </Tab>
</Tabs>

### `send()` is removed; use `raise()` or `sendTo()`

<Callout type="warning">
  Breaking change
</Callout>

The `send(...)` action creator is removed. Use `raise(...)` for sending events to self or `sendTo(...)` for sending events to other actors instead.

Read the documentation on the [`sendTo` action](actions.mdx#send-to-action) and [`raise` action](actions.mdx#raise-action) for more information.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      entry: [
        // Send an event to self
        raise({ type: 'someEvent' }),

        // Send an event to another actor
        sendTo('someActor', { type: 'someEvent' }),
      ],
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      entry: [
        // Send an event to self
        send({ type: 'someEvent' }),

        // Send an event to another actor
        send({ type: 'someEvent' }, { to: 'someActor' }),
      ],
    });
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update v4 projects to use `sendTo` or `raise` instead of `send`.

### Use `enqueueActions()` instead of `pure()` and `choose()`

The `pure()` and `choose()` methods have been removed. Use `enqueueActions()` instead.

For `pure()` actions:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    entry: [
      enqueueActions(({ context, event, enqueue }) => {
        enqueue('action1');
        enqueue('action2');
      }),
    ];
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    entry: [
      pure(() => {
        return ['action1', 'action2'];
      }),
    ];
    ```
  </Tab>
</Tabs>

For `choose()` actions:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    entry: [
      enqueueActions(({ enqueue, check }) => {
        if (check('someGuard')) {
          enqueue('action1');
          enqueue('action2');
        }
      }),
    ];
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    entry: [
      choose([
        {
          guard: 'someGuard',
          actions: ['action1', 'action2'],
        },
      ]),
    ];
    ```
  </Tab>
</Tabs>

### `actor.send()` no longer accepts string types

<Callout type="warning">
  Breaking change
</Callout>

String event types can no longer be sent to, e.g., `actor.send(event)`; you must send an event object instead:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    actor.send({ type: 'someEvent' });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    actor.send('someEvent');
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update v4 projects to pass an object to `.send()`.

### `state.can()` no longer accepts string types

<Callout type="warning">
  Breaking change
</Callout>

String event types can no longer be sent to, e.g., `state.can(event)`; you must send an event object instead:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    state.can({ type: 'someEvent' });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    state.can('someEvent');
    ```
  </Tab>
</Tabs>

### Guarded transitions use `guard`, not `cond`

<Callout type="warning">
  Breaking change
</Callout>

The `cond` transition property for guarded transitions is now called `guard`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      on: {
        someEvent: {
          guard: 'someGuard',
          target: 'someState',
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      on: {
        someEvent: {
          // renamed to `guard` in v5
          cond: 'someGuard',
          target: 'someState',
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Use `params` to pass params to actions & guards

<Callout type="warning">
  Breaking change
</Callout>

Properties other than `type` on action objects and guard objects should be nested under a `params` property; `{ type: 'someType', message: 'hello' }` becomes `{ type: 'someType', params: { message: 'hello' }}`. These `params` are then passed to the 2nd argument of the action or guard implementation:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      entry: {
        type: 'greet',
        params: {
          message: 'Hello world',
        },
      },
      on: {
        someEvent: {
          guard: { type: 'isGreaterThan', params: { value: 42 } },
        },
      },
    }).provide({
      actions: {
        greet: ({ context, event }, params) => {
          console.log(params.message); // 'Hello world'
        },
      },
      guards: {
        isGreaterThan: ({ context, event }, params) => {
          return event.value > params.value;
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine(
      {
        entry: {
          type: 'greet',
          message: 'Hello world',
        },
        on: {
          someEvent: {
            cond: { type: 'isGreaterThan', value: 42 },
          },
        },
      },
      {
        actions: {
          greet: (context, event, { action }) => {
            console.log(action.message); // 'Hello world'
          },
        },
        guards: {
          isGreaterThan: (context, event, { guard }) => {
            return event.value > guard.value;
          },
        },
      },
    );
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update action and guard objects on v4 projects to move properties (other than `type`) to a `params` object.

### Use wildcard `*` transitions, not strict mode

<Callout type="warning">
  Breaking change
</Callout>

Strict mode is removed. If you want to throw on unhandled events, you should use a wildcard transition:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      on: {
        knownEvent: {
          // ...
        },
        // [!code highlight:6]
        '*': {
          // unknown event
          actions: ({ event }) => {
            throw new Error(`Unknown event: ${event.type}`);
          },
        },
      },
    });

    const actor = createActor(machine);

    actor.subscribe({
      // [!code highlight:3]
      error: (err) => {
        console.error(err);
      },
    });

    actor.start();

    actor.send({ type: 'unknownEvent' });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      strict: true,
      on: {
        knownEvent: {
          // ...
        },
      },
    });

    const service = interpret(machine);

    service.send({ type: 'unknownEvent' });
    ```
  </Tab>
</Tabs>

### Use explicit eventless (`always`) transitions

<Callout type="warning">
  Breaking change
</Callout>

Eventless (“always”) transitions must now be defined through the `always: { ... }` property of a state node; they can no longer be defined via an empty string:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      states: {
        someState: {
          always: {
            target: 'anotherState',
          },
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      states: {
        someState: {
          on: {
            '': {
              target: 'anotherState',
            },
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update v4 projects to use `always` for *eventless* transitions.

### Use `reenter: true`, not `internal: false`

<Callout type="warning">
  Breaking change
</Callout>

`internal: false` is now `reenter: true`

External transitions previously specified with `internal: false` are now specified with `reenter: true`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      on: {
        someEvent: {
          target: 'sameState',
          reenter: true,
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      on: {
        someEvent: {
          target: 'sameState',
          internal: false,
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Transitions are internal by default, not external

<Callout type="warning">
  Breaking change
</Callout>

All transitions are **internal by default**. This change is relevant for transitions defined on state nodes with `entry` or `exit` actions, invoked actors, or delayed transitions (`after`). If you relied on the previous XState v4 behavior where transitions implicitly re-entered a state node, use `reenter: true`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          entry: 'someAction',
          on: {
            // [!code highlight:10]
            someEvent: {
              target: 'compoundState.childState',
              // Reenters the `compoundState` state,
              // just like an external transition
              reenter: true,
            },
            selfEvent: {
              target: 'childState',
              reenter: true,
            },
          },
          initial: 'childState',
          states: {
            childState: {},
          },
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          entry: 'someAction',
          on: {
            // [!code highlight:7]
            someEvent: {
              // implicitly external
              target: 'compoundState.childState', // non-relative target
            },
            selfEvent: {
              target: 'compoundState',
            },
          },
          initial: 'childState',
          states: {
            childState: {},
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          after: {
            1000: {
              target: 'compoundState.childState',
              reenter: true, // make it external explicitly!
            },
          },
          initial: 'childState',
          states: {
            childState: {},
          },
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          after: {
            1000: {
              // implicitly external
              target: 'compoundState.childState', // non-relative target
            },
          },
          initial: 'childState',
          states: {
            childState: {},
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Child state nodes are always re-entered

<Callout type="warning">
  Breaking change
</Callout>

Child state nodes are always re-entered when they are targeted by transitions (both external and internal) defined on compound state nodes. This change is relevant only if a child state node has `entry` or `exit` actions, invoked actors, or delayed transitions (`after`). Add a `stateIn` guard to prevent undesirable re-entry of the child state:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅

    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          on: {
            someEvent: {
              guard: not(stateIn({ compoundState: 'childState' })),
              target: '.childState',
            },
          },
          initial: 'childState',
          states: {
            childState: {
              entry: 'someAction',
            },
          },
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED

    const machine = createMachine({
      // ...
      states: {
        compoundState: {
          on: {
            someEvent: {
              // Implicitly internal; childState not re-entered
              target: '.childState',
            },
          },
          initial: 'childState',
          states: {
            childState: {
              entry: 'someAction',
            },
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Use `stateIn()` to validate state transitions, not `in`

<Callout type="warning">
  Breaking change
</Callout>

The `in: 'someState'` transition property is removed. Use `guard: stateIn(...)` instead:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      on: {
        someEvent: {
          guard: stateIn({ form: 'submitting' }),
          target: 'someState',
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      on: {
        someEvent: {
          in: '#someMachine.form.submitting'
          target: 'someState',
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Use `actor.subscribe()` instead of `state.history`

<Callout type="warning">
  Breaking change
</Callout>

The `state.history` property is removed. If you want the previous snapshot, you should maintain that via `actor.subscribe(...)` instead.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    let previousSnapshot = actor.getSnapshot();

    actor.subscribe((snapshot) => {
      doSomeComparison(previousSnapshot, snapshot);
      previousSnapshot = snapshot;
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    actor.subscribe((state) => {
      doSomeComparison(state.history, state);
    });
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update v4 projects to track history using `actor.subscribe()`.

### Actions can throw errors without `escalate`

<Callout type="warning">
  Breaking change
</Callout>

The `escalate` action creator is removed. In XState v5 actions can throw errors, and they will propagate as expected. Errors can be handled using an `onError` transition.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const childMachine = createMachine({
      // This will be sent to the parent machine that invokes this child
      // [!code highlight:3]
      entry: () => {
        throw new Error('This is some error');
      },
    });

    const parentMachine = createMachine({
      invoke: {
        src: childMachine,
        // [!code highlight:11]
        onError: {
          actions: ({ context, event }) => {
            console.log(event.error);
            //  {
            //    type: ...,
            //    error: {
            //      message: 'This is some error'
            //    }
            //  }
          },
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const childMachine = createMachine({
      // [!code highlight:1]
      entry: escalate('This is some error'),
    });

    /* ... */
    ```
  </Tab>
</Tabs>

## Actors

### Use actor logic creators for `invoke.src` instead of functions

<Callout type="warning">
  Breaking change
</Callout>

The available actor logic creators are:

* `createMachine`
* `fromPromise`
* `fromObservable`
* `fromEventObservable`
* `fromTransition`
* `fromCallback`

See [Actors](actors) for more information.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import { fromPromise, setup } from 'xstate';

    const machine = setup({
      actors: {
        getUser: fromPromise(async ({ input }: { input: { userId: string } }) => {
          const data = await getData(input.userId);
          // ...
          return data;
        }),
      },
    }).createMachine({
      invoke: {
        src: 'getUser',
        input: ({ context, event }) => ({
          userId: context.userId,
        }),
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { createMachine } from 'xstate';

    const machine = createMachine({
      invoke: {
        src: (context) => async () => {
          const data = await getData(context.userId);

          // ...
          return data;
        },
      },
    });
    ```
  </Tab>
</Tabs>

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import { fromCallback, createMachine } from 'xstate';

    const machine = createMachine({
      invoke: {
        src: fromCallback(({ sendBack, receive, input }) => {
          // ...
        }),
        input: ({ context, event }) => ({
          userId: context.userId,
        }),
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { createMachine } from 'xstate';

    const machine = createMachine({
      invoke: {
        src: (context, event) => (sendBack, receive) => {
          // context.userId
          // ...
        },
      },
    });
    ```
  </Tab>
</Tabs>

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import { fromEventObservable, createMachine } from 'xstate';
    import { interval, mapTo } from 'rxjs';

    const machine = createMachine({
      invoke: {
        src: fromEventObservable(() =>
          interval(1000).pipe(mapTo({ type: 'tick' })),
        ),
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { createMachine } from 'xstate';
    import { interval, mapTo } from 'rxjs';

    const machine = createMachine({
      invoke: {
        src: () => interval(1000).pipe(mapTo({ type: 'tick' })),
      },
    });
    ```
  </Tab>
</Tabs>

### Use `invoke.input` instead of `invoke.data`

<Callout type="warning">
  Breaking change
</Callout>

The `invoke.data` property is removed. If you want to provide context to invoked actors, use `invoke.input`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const someActor = createMachine({
      // The input must be consumed by the invoked actor:
      context: ({ input }) => input,
      // ...
    });

    const machine = createMachine({
      // ...
      invoke: {
        src: 'someActor',
        input: {
          value: 42,
        },
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const someActor = createMachine({
      // ...
    });

    const machine = createMachine({
      // ...
      invoke: {
        src: 'someActor',
        data: {
          value: 42,
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Use `output` in final states instead of `data`

<Callout type="warning">
  Breaking change
</Callout>

To produce output data from a machine which reached its final state, use the top-level `output` property instead of `data`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
        },
      },
      output: {
        answer: 42,
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
          data: {
            answer: 42,
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

To provide a dynamically generated output, replace `invoke.data` with `invoke.output` and add a top-level `output` property:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
          output: ({ event }) => ({
            answer: event.someValue,
          }),
        },
      },
      output: ({ event }) => event.output,
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
          data: (context, event) => {
            answer: event.someValue,
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Don't use property mappers in `input` or `output`

<Callout type="warning">
  Breaking change
</Callout>

If you want to provide dynamic context to invoked actors, or produce dynamic output from final states, use a function instead of an object with property mappers.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      // ...
      invoke: {
        src: 'someActor',
        input: ({ context, event }) => ({
          value: event.value,
        }),
      },
    });

    // The input must be consumed by the invoked actor:
    const someActor = createMachine({
      // ...
      context: ({ input }) => input,
    });

    // Producing machine output
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
        },
      },
      output: ({ context, event }) => ({
        answer: context.value,
      }),
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      // ...
      invoke: {
        src: 'someActor',
        data: {
          value: (context, event) => event.value, // a property mapper
        },
      },
    });

    // Producing machine output
    const machine = createMachine({
      // ...
      states: {
        finished: {
          type: 'final',
          data: {
            answer: (context, event) => context.value, // a property mapper
          },
        },
      },
    });
    ```
  </Tab>
</Tabs>

### Use `actors` property on `options` object instead of `services`

<Callout type="warning">
  Breaking change
</Callout>

`services` have been renamed to `actors`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const specificMachine = machine.provide({
      actions: {
        /* ... */
      },
      guards: {
        /* ... */
      },
      actors: {
        /* ... */
      },
      // ...
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const specificMachine = machine.withConfig({
      actions: {
        /* ... */
      },
      guards: {
        /* ... */
      },
      services: {
        /* ... */
      },
      // ...
    });
    ```
  </Tab>
</Tabs>

### Use `subscribe()` for changes, not `onTransition()`

<Callout type="warning">
  Breaking change
</Callout>

The `actor.onTransition(...)` method is removed. Use `actor.subscribe(...)` instead.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const actor = createActor(machine);
    actor.subscribe((state) => {
      // ...
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const actor = interpret(machine);
    actor.onTransition((state) => {
      // ...
    });
    ```
  </Tab>
</Tabs>

### `createActor()` (formerly `interpret()`) accepts a second argument to restore state

<Callout type="warning">
  Breaking change
</Callout>

`interpret(machine).start(state)` is now `createActor(machine, { snapshot }).start()`

To restore an actor at a specific state, you should now pass the state as the `snapshot` property of the `options` argument of `createActor(logic, options)`. The `actor.start()` property no longer takes in a `state` argument.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const actor = createActor(machine, { snapshot: someState });
    actor.start();
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const actor = interpret(machine);
    actor.start(someState);
    ```
  </Tab>
</Tabs>

### Use `actor.getSnapshot()` to get actor’s state

<Callout type="warning">
  Breaking change
</Callout>

Subscribing to an actor (`actor.subscribe(...)`) after the actor has started will no longer emit the current snapshot immediately. Instead, read the current snapshot from `actor.getSnapshot()`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const actor = createActor(machine);
    actor.start();

    const initialState = actor.getSnapshot();

    actor.subscribe((state) => {
      // Snapshots from when the subscription was created
      // Will not emit the current snapshot until a transition happens
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const actor = interpret(machine);
    actor.start();

    actor.subscribe((state) => {
      // Current snapshot immediately emitted
    });
    ```
  </Tab>
</Tabs>

### Loop over events instead of using `actor.batch()`

<Callout type="warning">
  Breaking change
</Callout>

The `actor.batch([...])` method for batching events is removed.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    for (const event of events) {
      actor.send(event);
    }
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    actor.batch(events);
    ```
  </Tab>
</Tabs>

**Pre-migration tip:** Update v4 projects to loop over events to send them as a batch.

### Use `snapshot.status === 'done'` instead of `snapshot.done`

<Callout type="warning">
  Breaking change
</Callout>

The `snapshot.done` property, which was previously in the snapshot object of state machine actors, is removed. Use `snapshot.status === 'done'` instead, which is available to all actors:

<Tabs>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const actor = createActor(machine);
    actor.start();

    actor.subscribe((snapshot) => {
      if (snapshot.status === 'done') {
        // ...
      }
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const actor = interpret(machine);
    actor.start();

    actor.subscribe((state) => {
      if (state.done) {
        // ...
      }
    });
    ```
  </Tab>
</Tabs>

### `state.nextEvents` has been removed

<Callout type="warning">
  Breaking change
</Callout>

The `state.nextEvents` property is removed, since it is not a completely safe/reliable way of determining the next events that can be sent to the actor. If you want to get the next events according to the previous behavior, you can use this helper function:

```ts
import type { AnyMachineSnapshot } from 'xstate';

function getNextEvents(snapshot: AnyMachineSnapshot) {
  return [...new Set([...snapshot._nodes.flatMap((sn) => sn.ownEvents)])];
}

// Instead of `state.nextEvents`:
const nextEvents = getNextEvents(state);
```

## TypeScript

### Use `types` instead of `schema`

<Callout type="warning">
  Breaking change
</Callout>

The `machineConfig.schema` property is renamed to `machineConfig.types`:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      types: {} as {
        context: {
          /* ...*/
        };
        events: {
          /* ...*/
        };
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      schema: {} as {
        context: {
          /* ...*/
        };
        events: {
          /* ...*/
        };
      },
    });
    ```
  </Tab>
</Tabs>

### Use `types.typegen` instead of `tsTypes`

<Callout type="warning">
  Breaking change
</Callout>

<Callout>
  XState Typegen does not fully support XState v5 yet. However, strongly-typed machines can still be achieved without Typegen.
</Callout>

The `machineConfig.tsTypes` property has been renamed and is now at `machineConfig.types.typegen`.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    const machine = createMachine({
      types: {} as {
        typegen: {};
        context: {
          /* ...*/
        };
        events: {
          /* ...*/
        };
      },
    });
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    const machine = createMachine({
      tsTypes: {};
      schema: {} as {
        context: {
          /* ...*/
        };
        events: {
          /* ...*/
        };
      },
    });
    ```
  </Tab>
</Tabs>

## `@xstate/react`

### `useInterpret()` is now `useActorRef()`

<Callout type="warning">
  Breaking change
</Callout>

The `useInterpret()` hook, which is used to return an `actorRef` ("service" in XState v4), is renamed to `useActorRef()`.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```ts
    // ✅
    import { useActorRef } from '@xstate/react';

    const actorRef = useActorRef(machine); // or any other logic
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```ts
    // ❌ DEPRECATED
    import { useInterpret } from '@xstate/react';

    const service = useInterpret(machine);
    ```
  </Tab>
</Tabs>

### `useActor(logic)` now accepts actor logic, not an actor

<Callout type="warning">
  Breaking change
</Callout>

The `useActor(logic)` hook now accepts *actor logic* (such as `fromPromise(...)`, `createMachine(...)`, etc.) instead of an existing `ActorRef`.

To use an existing `ActorRef`, use `actor.send(...)` to send events and `useSelector(actor, ...)` to get the snapshot:

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```tsx
    // ✅
    import { useSelector } from '@xstate/react';

    function Component({ someActorRef }) {
      const state = useSelector(someActorRef, (s) => s);

      return <button onClick={() => someActorRef.send({ type: 'someEvent' })} />;
    }
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```tsx
    // ❌ DEPRECATED
    import { useActor } from '@xstate/react';

    function Component({ someActorRef }) {
      const [state, send] = useActor(someActorRef);

      return <button onClick={() => send({ type: 'someEvent' })} />;
    }
    ```
  </Tab>
</Tabs>

## Use `machine.provide()` to provide implementations in hooks

<Callout type="warning">
  Breaking change
</Callout>

For dynamically creating machines with provided implementations, the `useMachine(...)`, `useActor(...)`, and `useActorRef(...)` hooks no longer accept:

* Lazy machine creators as the 1st argument
* Implementations passed to the 2nd argument

Instead, `machine.provide(...)` should be passed directly to the 1st argument.

The `@xstate/react` package considers machines with the same configuration to be the same machine, so it will minimize rerenders but still keep the provided implementations up-to-date.

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```tsx
    // ✅
    import { useMachine } from '@xstate/react';
    import { someMachine } from './someMachine';

    function Component(props) {
      const [state, send] = useMachine(
        someMachine.provide({
          actions: {
            doSomething: () => {
              props.onSomething?.(); // Kept up-to-date
            },
          },
        }),
      );

      // ...
    }
    ```
  </Tab>

  <Tab value="v4 arguments" label="XState v4">
    ```tsx
    // ❌ DEPRECATED
    import { useMachine } from '@xstate/react';
    import { someMachine } from './someMachine';

    function Component(props) {
      const [state, send] = useMachine(someMachine, {
        actions: {
          doSomething: () => {
            props.onSomething?.();
          },
        },
      });

      // ...
    }
    ```
  </Tab>

  <Tab value="v4 function" label="XState v4">
    ```tsx
    // ❌ DEPRECATED
    import { useMachine } from '@xstate/react';
    import { someMachine } from './someMachine';

    function Component(props) {
      const [state, send] = useMachine(() =>
        someMachine.withConfig({
          actions: {
            doSomething: () => {
              props.onSomething?.();
            },
          },
        }),
      );

      // ...
    }
    ```
  </Tab>
</Tabs>

## `@xstate/vue`

### `useMachine()` now returns `snapshot` instead of `state`, and `actor` instead of `service`

<Callout type="warning">
  Breaking change
</Callout>

To keep consistent naming with the rest of XState and related libraries:

* `state` is now `snapshot`
* `service` is now `actor`

<Tabs items={['v5', 'v4']}>
  <Tab value="v5" label="XState v5">
    ```tsx
    // ✅
    import { useMachine } from '@xstate/vue';

    // ...

    const {
      // [!code highlight:1]
      snapshot, // Renamed from `state`
      send,
      // [!code highlight:1]
      actor, // Renamed from `service`
    } = useMachine(someMachine);
    ```
  </Tab>

  <Tab value="v4" label="XState v4">
    ```tsx
    // ❌ DEPRECATED
    import { useMachine } from '@xstate/vue';

    // ...

    const {
      // [!code highlight:1]
      state, // Renamed to `snapshot` in @xstate/vue 3.0.0
      send,
      // [!code highlight:1]
      service, // Renamed to `actor` in @xstate/vue 3.0.0
    } = useMachine(someMachine);
    ```
  </Tab>
</Tabs>

## New features

* [Create actor systems](system)
* [New actor logic creators](/docs/actors#actor-logic-creators)
* [Deep persistence for invoked and spawned actors](persistence)
* [Provide input data to state machines and actors](input)
* [Specify output “done data” for actors](output)
* [Partial event descriptors (partial wildcards)](/docs/transitions#partial-wildcard-transitions)
* [Enqueue actions](/docs/actions#enqueue-actions)
* [Higher-level guards](/docs/guards#higher-level-guards)
* [Setup API for specifying types and strongly-typed state values](/docs/machines#providing-implementations)
* [Inspect API](inspection)

## Frequently asked questions

### When will Stately Studio be compatible with XState v5?

We are currently working on [Stately Studio](studio) compatibility with XState v5. Exporting to XState v5 (JavaScript or TypeScript) is already available. We are working on support for new XState v5 features, such as higher-order guards, partial event wildcards, and machine input/output.

Upvote or comment on [Stately Studio + XState v5 compatibility in our roadmap](https://feedback.stately.ai/editor/p/stately-studio-xstate-v5-compatibility) to stay updated on our progress.

### When will the XState VS Code extension be compatible with XState v5?

The [XState VS Code extension](xstate-vscode-extension) is not yet compatible with XState v5. The extension is a priority for us, and work is already underway.

Upvote or comment on [XState v5 compatibility for VS Code extension in our roadmap](https://feedback.stately.ai/devtools/p/xstate-v5-compatibility-for-vs-code-extension) to stay updated on our progress.

### When will XState v5 have typegen?

TypeScript inference has been greatly improved in XState v5. Especially with features like the `setup()` API and dynamic parameters, the main use-cases for typegen are no longer needed.

However, we recognize that there may still be some specific use-cases for typegen. Upvote or comment on [Typegen for XState v5 in our roadmap](https://feedback.stately.ai/xstate/p/typegen-for-xstate-v5) to stay updated on our progress.

### How can I use both XState v4 and v5?

You can use both XState v4 and v5 in the same project, which is useful for incrementally migrating to XState v5. To use both, add `"xstate5": "npm:xstate@5"` to your `package.json` manually or through the CLI:

```bash
npm i xstate5@npm:xstate@5
```

Then, you can import the v5 version of XState in your code:

```ts
import { createMachine } from 'xstate5';
// or { createMachine as createMachine5 } from 'xstate5';
```

If you need to use different versions of an integration package, such as `@xstate/react`, you can use a similar strategy as above, but you will need to link to the correct version of XState in the integration package. This can be done by using a script:

```bash
npm i xstate5@npm:xstate@5 @xstate5/react@npm:@xstate/react@4 --force
```

```js
// scripts/xstate5-react-script.js
const fs = require('fs-extra');
const path = require('path');

const rootNodeModules = path.join(__dirname, '..', 'node_modules');

fs.ensureSymlinkSync(
  path.join(rootNodeModules, 'xstate5'),
  path.join(rootNodeModules, '@xstate5', 'react', 'node_modules', 'xstate'),
);
```

```json5
// package.json
"scripts": {
  "postinstall": "node scripts/xstate5-react-script.js"
}
```

Then, you can use the XState v5 compatible version of `@xstate/react` in your code:

```ts
import { useMachine } from '@xstate5/react';
// or { useMachine as useMachine5 } from '@xstate5/react';
import { createMachine } from 'xstate5';
// or { createMachine as createMachine5 } from 'xstate5';

// ...
```


# Observable Actors (/docs/observable-actors)

Observable actors are actors that represent an observable stream of values. This makes it easy to interop with observable libraries like RxJS.

<Callout>
  All XState actors are observable.
</Callout>

## Observable actor capabilities

|   | Capability     | Notes                                                                                                                                   |
| - | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| ❌ | Receive events | Observable actors do not currently receive events.                                                                                      |
| ✅ | Send events    | Observable actors can send events to other actors it has reference to, such as those provided in its `input`.                           |
| ❌ | Spawn actors   | Observable actors currently cannot spawn new actors.                                                                                    |
| ✅ | Input          | You can provide `input` to observable actors.                                                                                           |
| ❌ | Output         | Observable actors currently do not produce output – they are active indefinitely until they are stopped, completed, or an error occurs. |

## Observable actor logic

You can define observable actor logic using the `fromObservable(...)` actor logic creator, which takes a function that returns an observable and returns actor logic that can be used to create observable actors.

```ts
import { fromObservable, createActor } from 'xstate';
import { interval } from 'rxjs';

// [!code highlight:1]
const intervalLogic = fromObservable(() => interval(1000));

const intervalActor = createActor(intervalLogic);
intervalActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});

intervalActor.start();
// logs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
// every second
```

## Observable actor input

You can pass in `input` to an observable actor by passing it to the `createActor(...)` function as the `input` property of the second argument. In the observable logic (`fromObservable(observableFn)`), you read the `input` property of the first argument passed to the observable function:

```ts
import { fromObservable, createActor } from 'xstate';
import { interval } from 'rxjs';

// [!code highlight:1]
const intervalLogic = fromObservable(({ input }) => interval(input.interval));

const intervalActor = createActor(intervalLogic, {
  // [!code highlight:1]
  input: { interval: 10_000 },
});
intervalActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});

intervalActor.start();
// logs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
// every 10 seconds
```


# Output (/docs/output)

Output refers to the final data that an [actor](actors) produces. When an actor is responsible for performing some task, such as making a network request or running complex calculations, it will return output once it finishes that task. The output represents the result of the actor’s work. Actors only produce output when their status is “done”; i.e. when they are in their [final state](final-states).

Some actors may run indefinitely and not produce any output.

## Output in machine actors

Read [final states](final-states) for more information on how to specify output in machine actors.

## Output in promise actors

When a promise actor resolves, it can produce output data. You can specify this output data in the `.output` property of the machine:

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(async () => {
  const response = await fetch('https://example.com');

  return response.json() as { message: string };
});

const actor = createActor(promiseLogic);

actor.subscribe((snapshot) => {
  if (snapshot.status === 'done') {
    console.log(snapshot.output.message);
  }
});

actor.start();
```

## Output and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

The `output` property can be typed in the `.types` property of the machine setup:

```ts
import { setup, createActor } from 'xstate';

const machine = setup({
  // [!code highlight:3]
  types: {
    output: {} as { total: number },
  },
}).createMachine({
  // ...
  output: ({ context }) => ({
    total: context.items.reduce((total, item) => total + item.price, 0),
  }),
});

const actor = createActor(machine);

actor.subscribe((snapshot) => {
  if (snapshot.status === 'done') {
    console.log(snapshot.output.total);
  }
});
```

You can read the output type from any actor using `OutputFrom<typeof actor>`:

```ts
import type { OutputFrom } from 'xstate';
import { machine } from './machine';

const actor = createActor(machine);

function acceptOutput<T>(output: OutputFrom<typeof actor>) {
  console.log(output.total);
}
```


# Parallel states (/docs/parallel-states)

In statecharts, a parallel state is a state that has multiple child states (also known as **regions**) that are all active at the same time. This is different from a [parent state](parent-states), where only *one* child state is active at a time.

Parallel states have the following characteristics:

* Entering a parallel state will also simultaneously enter all of its regions.
* Exiting a parallel state will also simultaneously exit all of its regions.
* An event received in a parallel state is simultaneously received & handled by all of its regions.

<Callout>
  You can easily visualize and simulate parallel states in Stately's editor. [Read more about parallel states in Stately's editor](/docs/editor-states-and-transitions/#parallel-states).
</Callout>

Here is a music player example with a parallel state consisting of two regions, one for handling playing the track and one for handling the volume:

```ts
import { createMachine, assign } from 'xstate';

const playerMachine = createMachine({
  id: 'player',
  // [!code highlight:1]
  type: 'parallel',
  states: {
    track: {
      initial: 'paused',
      states: {
        paused: {
          on: { PLAY: 'playing' },
        },
        playing: {
          on: { STOP: 'paused' },
        },
      },
    },
    volume: {
      initial: 'normal',
      states: {
        normal: {
          on: { MUTE: 'muted' },
        },
        muted: {
          on: { UNMUTE: 'normal' },
        },
      },
    },
  },
});
```

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=733de338-26cb-40a5-a0b5-b76bfc0405c3" />

## Parallel state value

The [state value](states.mdx#state-value) of a parallel state is an object with the state values of each of its regions.

```ts
const playerActor = createActor(playerMachine);
playerActor.start();

console.log(playerActor.getSnapshot().value);
// logs the object:
// {
//   track: 'paused',
//   volume: 'normal'
// }
```

## Parallel onDone transition

When all regions of a parallel state have reached their final states, the `onDone` transition of the parallel state is taken.

In this example, the `onDone` transition of the parallel state is taken when both regions have reached their final states; that is, when `'grindingBeans'` reaches the `'grindingBeans.beansGround'` state and `'boilingWater'` reaches the `'boilingWater.waterBoiled'` state.

```ts
import { createMachine } from 'xstate';

export const machine = createMachine({
  id: 'coffee',
  initial: 'preparing',
  states: {
    preparing: {
      states: {
        grindBeans: {
          initial: 'grindingBeans',
          states: {
            grindingBeans: {
              on: {
                BEANS_GROUND: {
                  target: 'beansGround',
                },
              },
            },
            beansGround: {
              // [!code highlight:1]
              type: 'final',
            },
          },
        },
        boilWater: {
          initial: 'boilingWater',
          states: {
            boilingWater: {
              on: {
                WATER_BOILED: {
                  target: 'waterBoiled',
                },
              },
            },
            waterBoiled: {
              // [!code highlight:1]
              type: 'final',
            },
          },
        },
      },
      type: 'parallel',
      // [!code highlight:3]
      onDone: {
        target: 'makingCoffee',
      },
    },
    makingCoffee: {},
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=187dcde8-efed-4106-bfa7-59afadb2297f&mode=Simulate" />

## Modeling

When modeling with parallel states, there are several important considerations to keep in mind:

### Best Practices

* **Avoid transitions between regions**: Each region in a parallel state should be independent and not directly transition to states in other regions. This maintains the separation of concerns and prevents complex interdependencies.

* **Use for related concerns**: Parallel states are ideal for modeling aspects of a system that are related but operate independently. For example:

  * A media player's playback state and volume control
  * A form's validation state and submission state
  * A game's player state and game state

* **Consider synchronization**: While regions operate independently, they may need to coordinate. Use events that can be handled by multiple regions to achieve synchronization when needed.

### When to Use Parallel States

* When you have multiple independent aspects of your system that need to be tracked simultaneously
* When these aspects may need to coordinate or affect each other
* When the system needs to maintain multiple active states at once

### When to Use Alternatives

* **Use `invoke` instead** when the concerns are completely separate and don't need to coordinate
* **Use nested states** when the states are hierarchical and only one should be active at a time
* **Use a single state** with multiple properties when the aspects are tightly coupled

### Example Use Cases

* **User Interface**: Managing multiple independent UI states (e.g., sidebar, modal, and main content)
* **Game Development**: Tracking player state, game state, and UI state simultaneously
* **Form Handling**: Managing validation, submission, and error states in parallel
* **Media Players**: Controlling playback, volume, and playlist states independently

## Parallel states cheatsheet

### Cheatsheet: create a parallel state

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  // ...
  states: {
    type: 'parallel',
    states: {
      one: {
        /* ... */
      },
      two: {
        /* ... */
      },
      three: {
        /* ... */
      },
    },
    onDone: {
      // Taken when all regions have reached their final states
    },
  },
});
```


# Parent states (/docs/parent-states)

States can contain more states, also known as **child states**. These child states are only active when the parent state is active.

Child states are nested inside their parent states. Parent states are also known as **compound states**.

<Callout>
  You can easily visualize and simulate parent and child states in Stately's editor. [Read more about parent states in Stately's editor](/docs/editor-states-and-transitions/#parent-and-child-states).
</Callout>

<Callout>
  Watch our ["Parent and child states" video on YouTube](https://www.youtube.com/watch?v=aUhEdeIf_mQ\&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ\&index=5) (1m6s).
</Callout>

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=9ba5377c-aab3-4465-8909-4eea499622fa" />

In the video player above, the *Opened* state is a parent state to the *Playing*, *Paused*, and *Stopped* states. These states, their transitions, and their events are nested inside the *Opened* state.

## Root state

The state machine itself is a parent state! It's the root state, and it's always active.

It's normal to have a state machine that has no other states. This is useful for modeling a simple state machine that only handles events by executing actions in transitions.

Here is an example of a simple counting machine with `increment`, `decrement`, and `reset` events, and no states, other than the implicit top-level root state:

```ts
import { createMachine, assign } from 'xstate';

const countingMachine = createMachine({
  id: 'counting',
  on: {
    increment: {
      actions: assign({ count: ({ context }) => context.count + 1 }),
    },
    decrement: {
      actions: assign({ count: ({ context }) => context.count - 1 }),
    },
    reset: {
      actions: assign({ count: 0 }),
    },
  },
  // No child states!
});
```

## Initial state

The [initial state](initial-states) of a parent state is the state that is entered when the parent state is entered. Parent states *must* have an initial states.

You specify the initial state via the `initial` property of the parent state, which is the key of the initial state in the `states` object:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // [!code highlight:1]
  initial: 'question',
  states: {
    // [!code highlight:3]
    question: {
      // ...
    },
    form: {
      // ...
    },
    thanks: {
      // ...
    },
  },
});
```

Even if the parent state is never directly targeted and its child states are instead targeted, specifying the initial state in the `.initial` property is required. In this case, the `.initial` property can be any of the child states.

## Transitions on parent states

A transition that targets a parent state will enter the parent state and its initial state. If that initial state is a parent state, then that state's initial state will be entered, and so on.

When an event is received, transitions on the deepest child nodes are checked first to see if any of them are enabled by that event. If no transitions are enabled, then transitions on the parent state are checked. If no transitions on the parent state are enabled, then transitions on the parent's parent state is checked, and so on.

Transitions on a parent state can target child (or descendent) states. This is useful for modeling a transition that should go to a specific child state regardless of which child state is currently active.

Transitions on a child state can target the parent state, though this is not common. A transition from a child state to its parent (or ancestor) state will also enter the parent state's initial state.

## Child final states

When a child final state of a parent state is reached, that parent state is considered "done". The `onDone` transition of that parent state is automatically taken.

```ts
import { createMachine } from 'xstate';

const coffeeMachine = createMachine({
  initial: 'preparation',
  states: {
    preparation: {
      initial: 'weighing',
      states: {
        weighing: {
          on: {
            weighed: {
              target: 'grinding',
            },
          },
        },
        grinding: {
          on: {
            ground: 'ready',
          },
        },
        // [!code highlight:4]
        ready: {
          // Child final state of parent state 'preparation'
          type: 'final',
        },
      },
      // [!code highlight:4]
      // Transition will be taken when child final state is reached
      onDone: {
        target: 'brewing',
      },
    },
    brewing: {
      // ...
    },
  },
});
```

## Modeling

When designing state machines with parent states, follow these best practices:

### Start Flat, Then Nest

* Begin with a flat state structure and only introduce parent states when patterns emerge
* Avoid premature abstraction - let the state machine's behavior guide the structure
* Refactor to parent states when you notice repeated patterns or common behaviors

### Common Patterns for Parent States

* **Shared Transitions**: When multiple states share the same outgoing transitions, consider grouping them under a parent state
* **Sub-processes**: Use parent states to model distinct sub-processes or phases of your application
* **Common Entry/Exit**: If multiple states share entry or exit actions, they might belong in the same parent state
* **State Groups**: Group related states that represent different aspects of the same feature or component

### Parent State Design Tips

* Keep the hierarchy as shallow as possible - deep nesting can make the state machine harder to understand
* Each parent state should have a clear, single responsibility
* Use descriptive names that reflect the parent state's purpose
* Consider using parallel states for truly independent sub-processes
* Document the purpose of each parent state with comments

### When to Avoid Parent States

* When states don't share any common behavior or transitions
* When the hierarchy would make the state machine more complex without adding value
* When the states represent completely independent features

### Example: Form Validation

```ts
import { createMachine } from 'xstate';

const formMachine = createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        start: { target: 'validating' },
      },
    },
    validating: {
      initial: 'checking',
      states: {
        checking: {
          on: {
            valid: { target: 'success' },
            invalid: { target: 'error' },
          },
        },
        success: {
          type: 'final',
        },
        error: {
          on: {
            retry: { target: 'checking' },
          },
        },
      },
      onDone: { target: 'submitted' },
    },
    submitted: {
      type: 'final',
    },
  },
});
```

In this example, the `validating` parent state groups related states that handle the validation process, sharing common transitions and representing a clear sub-process.

## Parent states cheatsheet

### Cheatsheet: creating parent states

```ts
import { createMachine } from 'xstate';

// The machine is the root-level parent state
const machine = createMachine({
  // Initial child state of the machine
  initial: 'parent',
  states: {
    parent: {
      // Initial child state of the parent state
      initial: 'child1',
      states: {
        child1: {
          on: {
            // Targeting a sibling
            toSibling: {
              target: 'child2',
            },
          },
        },
        child2: {
          initial: 'grandchild1',
          states: {
            grandchild1: {},
            grandchild2: {},
          },
        },
      },
      on: {
        // Targeting a child
        toChild: {
          target: '.child1',
        },
        // Targeting a grandchild
        toGrandchild: {
          target: '.child2.grandchild2',
        },
      },
    },
  },
});
```


# Persistence (/docs/persistence)

[Actors](actors) can persist their internal state and restore it later. **Persistence** refers to storing the state of an actor in persistent storage, such as localStorage or a database. **Restoration** refers to restoring the state of an actor from persistent storage.

In frontend applications, persistence is useful for maintaining state across browser reloads. In backend applications, persistence allows workflows to span multiple requests, survive service restarts, be fault-tolerant, represent long-running processes, and be auditable and traceable.

In XState, you can obtain the snapshot (state) to be persisted via `actor.getPersistedSnapshot()` and restore it via `createActor(behavior, { snapshot: restoredState }).start()`:

```ts
const feedbackActor = createActor(feedbackMachine).start();

// Get state to be persisted
const persistedState = feedbackActor.getPersistedSnapshot();

// Persist state
localStorage.setItem('feedback', JSON.stringify(persistedState));

// Restore state
const restoredState = JSON.parse(localStorage.getItem('feedback'));

const restoredFeedbackActor = createActor(feedbackMachine, {
  snapshot: restoredState,
}).start();
```

## Persisting state

You can obtain the state to be persisted via `actor.getPersistedSnapshot()`:

```ts
const feedbackActor = createActor(feedbackMachine).start();

// Get state to be persisted
const persistedState = feedbackActor.getPersistedSnapshot();
```

The internal state can be persisted from any actor, not only machines. Note that the persisted state is *not* the same as the snapshot from `actor.getSnapshot()`; persisted state represents the internal state of the actor, while snapshots represent the actor's last emitted value:

```ts
const promiseActor = fromPromise(() => Promise.resolve(42));

// Get the last emitted value
const snapshot = promiseActor.getSnapshot();
console.log(snapshot);
// logs 42

// Get the persisted state
const persistedState = promiseActor.getPersistedSnapshot();
console.log(persistedState);
// logs { status: 'done', data: 42 }
```

## Restoring state

You can restore an actor to a persisted state by passing the persisted state into the `state` option of the second argument of `createActor(logic, { snapshot: restoredState })`:

```ts
// Get persisted state
const restoredState = JSON.parse(localStorage.getItem('feedback'));

// Restore state
const feedbackActor = createActor(feedbackMachine, {
  snapshot: restoredState,
});

feedbackActor.start();
```

Actions from machine actors will *not* be re-executed, because they are assumed to have been already executed. However, invocations will be restarted, and spawned actors will be restored recursively.

## Deep persistence

Persisting & restoring state from machine actors is deep; all [invoked](invoke) & [spawned actors](spawn) will be persisted and restored recursively.

```ts
const feedbackMachine = createMachine({
  // ...
  states: {
    form: {
      invoke: {
        id: 'form',
        src: formMachine,
      },
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

// Persist state
const persistedState = feedbackActor.getPersistedSnapshot();
localStorage.setItem('feedback', JSON.stringify(persistedState));

//  ...

// Restore state
const restoredState = JSON.parse(localStorage.getItem('feedback'));

const restoredFeedbackActor = createActor(feedbackMachine, {
  snapshot: restoredState,
}).start();
// Will restore both the feedbackActor and the invoked form actor at
// their persisted states
```

## Persisting state machine values

If you want to persist only the finite state `value` (and optionally the `context`) of a state machine actor, you can use the `machine.resolveState(...)` method:

```ts
import { someMachine } from './someMachine';

const restoredStateValue = localStorage.getItem('someState');
// Assume that this is "pending"

// [!code highlight:4]
const resolvedState = someMachine.resolveState({
  value: restoredStateValue,
  // context: { ... }
});

// Restore the actor
const restoredActor = createActor(someMachine, {
  // [!code highlight:1]
  snapshot: resolvedState,
});

restoredActor.start();
```

## Event sourcing

An alternative to persisting state is **event sourcing**, which is a way of restoring the state of an actor by replaying the [events](transitions) that led to that state. Event sourcing can be more reliable than persisting state, because it is less prone to [incompatible state](#caveats) and it also allows you to replay actions.

One way to implement event sourcing is to persist the events as they happen using the [inspection API](/docs/inspection), and then replay them to restore the state of the actor:

```ts
const events = [];

const someActor = createActor(someMachine, {
  // Inspect and persist events
  inspect: (inspectionEvent) => {
    if (inspectionEvent.type === '@xstate.event') {
      const event = inspectionEvent.event;

      // Only listen for events sent to the root actor
      if (inspectionEvent.actorRef !== someActor) {
        return;
      }

      // [!code highlight:1]
      events.push(event);
    }
  },
});

someActor.start();

// ...

// Assuming the events are stored somewhere, e.g. in localStorage,
// you can replay them to restore the state of the actor

const restoredActor = createActor(someMachine);
restoredActor.start();

// [!code highlight:4]
for (const event of events) {
  // Replay events
  restoredActor.send(event);
}
```

## Caveats

There are some caveats to persisting and restoring state that you should be aware of:

* Incompatible state: if the machine or actor logic changes, the restored state may be incompatible with the new logic.
* Replaying actions: actions that have already been executed will not be re-executed. [Event sourcing](#event-sourcing) is preferred for this use-case.
* Serialization: the state must be serializable, which means that it must be JSON-serializable. This means that you cannot persist functions, classes, or other non-serializable values.

## Persistence cheatsheet

### Cheatsheet: persisting state

```ts
const persistedState = actor.getPersistedSnapshot();
```

### Cheatsheet: restoring state

```ts
const restoredState = JSON.parse(localStorage.getItem('feedback'));

const restoredActor = createActor(actorMachine, {
  snapshot: restoredState,
}).start();
```

## Resources

* [Blog: Persisting state in XState](../../blog/2023-10-02-persisting-state)


# Projects (/docs/projects)





import { PlusSquare, MoreHorizontal } from 'lucide-react';

A project is a collection of machines that helps you organize your personal machines and collaborate with others on your application logic.

{/* <p>
  <ThemedImage
    alt="Stately Studio My Projects page showing three pages of projects along with each project’s name, number of machines, and whether the project is public, private, or unlisted."
    sources={{
      light: '/assets/projects/my-projects.png',
      dark: '/assets/projects/my-projects-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio My Projects page showing three pages of projects along with each project’s name, number of machines, and whether the project is public, private, or unlisted." src={__img0} placeholder="blur" />

You can access your projects from **My Projects** at the left of Stately Studio’s top bar when you’re signed into Stately Studio. **My Projects** lists your personal projects and is one of the locations where you can create new projects.

When you select a project, the first machine in the project will open in Stately Studio’s editor. The other machines in your project are accessible from the machines list in the left drawer menu.

{/* <p>
  <ThemedImage
    alt="Stately Studio’s editor showing the Machines list in the left drawer for Stately Studio Tutorials project. The selected machine is called ‘Parent states.’"
    sources={{
      light: '/assets/projects/machines-list.png',
      dark: '/assets/projects/machines-list-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio’s editor showing the Machines list in the left drawer for Stately Studio Tutorials project. The selected machine is called ‘Parent states.’" src={__img1} placeholder="blur" />

### Create a new project

You can create a new project when you first save a machine or from the **Create project** button in [**My Projects**](#my-projects). You must be signed in to Stately Studio to access **My Projects** and shared team projects, or to create new projects.

#### Create a new project from **My Projects**

Open **My Projects** from the link at the left of Stately Studio’s top bar and use the <PlusSquare size={18} /> **Create project** button.

#### Create a new project from Stately Studio’s editor

The **Save** button will save your machine or prompt you to sign into Stately Studio before saving. Your project will have the same name as your current machine name.

### Change a project’s visibility

A project can be **public**, **unlisted**, or **private**. If the project is a shared team project, only team members with the **Owner** or **Admin** roles can change its visibility.

<Callout>
  The project’s visibility is display in the lower left corner of the canvas.
</Callout>

* Public projects are visible to everyone. They are discoverable on the [Discover](discover) page and listed on your profile page.
* Unlisted projects are visible to everyone with the project’s URL but are not findable on the Discover page or listed on your profile page.
* Private projects are only visible to you and the team that owns the project.

Use the **Share** button in Stately Studio’s top bar and toggle the project’s visibility between **public**, **unlisted**, or **private** from the dropdown menu in the Share dialog.

<Callout>
  Unlisted and Private projects are a premium feature of Stately Studio. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

### Favorite a project

You can favorite your own projects and other people’s projects. Favoriting a project gives you easy access to these projects in the future, as your **Favorites** are listed in the left sidebar of Stately Studio’s workspace. You might want to favorite projects you find inspiring or educational so that you can refer back to them later.

You can favorite a project from **My Projects**, **Discover**, and team projects.

* Choose **Add to favorites** from the <MoreHorizontal size={18} /> triple dot contextual menu alongside the project’s name.
* Find your **Favorites** in the left sidebar of Stately Studio’s workspace.

#### Remove a project from your favorites

You can remove a project from your favorites from **Favorites**, **My Projects**, **Discover**, and team projects by choosing **Remove from favorites** from the <MoreHorizontal size={18} /> triple dot contextual menu alongside the project’s name.

## Move a project to a different team

You can move a project to a different team from the **Share** dialog. You can only move a project between teams if you have the [**Owner** role](teams.mdx#owner-role) on both teams.

## Move a machine to a different project

You can move a machine to a different team or to your personal projects from the <MoreHorizontal size={18} /> triple dot contextual menu alongside the machine’s name in the machines list. You can only move a machine between projects if you have the [**Owner** role](teams.mdx#owner-role), [**Admin** role](teams.mdx#admin-role), or [**Editor** role](teams.mdx#editor-role) in both projects.


# Promise actors (/docs/promise-actors)

Promise actors are actors that represent a promise that performs some asynchronous task. They may resolve with some output, or reject with an error.

## Promise actor capabilities

|   | Capability     | Notes                                                                                                      |
| - | -------------- | ---------------------------------------------------------------------------------------------------------- |
| ❌ | Receive events | Promise actors currently do not receive events.                                                            |
| ✅ | Send events    | Promise actors can send events to other actors it has reference to, such as those provided in its `input`. |
| ❌ | Spawn actors   | Promise actors currently cannot spawn new actors.                                                          |
| ✅ | Input          | You can provide `input` to promise actors.                                                                 |
| ✅ | Output         | Promise actors can produce `output`, which is the resolved value of the promise.                           |

## Promise actor logic

You can define promise actor logic using the `fromPromise(...)` actor logic creator, which takes a function that returns a promise and returns actor logic that can be used to create promise actors.

```ts
import { fromPromise, createActor } from 'xstate';

async function getUser(id: string) {
  // ...
  return { id /* other user data */ };
}

// [!code highlight:5]
const promiseLogic = fromPromise(async () => {
  const user = await getUser('123');

  return user;
});

const promiseActor = createActor(promiseLogic);

promiseActor.subscribe((snapshot) => {
  console.log(snapshot.status, snapshot.output);
});

promiseActor.start();

// logs 'active', undefined
// ... (after some time)
// logs 'done', { id: '123', /* other user data */ }
```

## Promise actor input

You can pass in `input` to a promise actor by passing it to the `createActor(...)` function as the `input` property of the second argument. In the promise logic (`fromPromise(promiseFn)`), you read the `input` property of the first argument passed to the promise function:

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(async ({ input }) => {
  const user = await getUser(input.id);

  return user;
});

const promiseActor = createActor(promiseLogic, {
  // [!code highlight:1]
  input: { id: '123' },
});
```

The `input` type is inferred from the promise function's first argument type. You can also provide an explicit `input` type in the second generic parameter:

```ts
import { fromPromise } from 'xstate';

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

const secondLogic = fromPromise(
  async ({ input }: { input: { id: string } }) => {
    const user = await getUser(input.id); // User is inferred

    return user;
  },
);

const firstLogic = fromPromise<User, { id: string }>(
  async ({ input, self /* ... */ }) => {
    const user = await getUser(input.id);

    return user;
  },
);
```

## Promise actor output

To get the eventual resolved `output` of a promise actor, you can subscribe to the promise actor and check the `status` property of the snapshot. If the `status` is `'done'`, you can access the `output` property of the snapshot to get the resolved value. Otherwise, the `output` will be `undefined`.

```ts
import { fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
  const user = await getUser('123');

  return user;
});

const promiseActor = createActor(promiseLogic);

promiseActor.subscribe((snapshot) => {
  if (snapshot.status === 'done') {
    console.log(snapshot.output);
    // logs { id: '123', /* other user data */ }
  }
});
```

You can also use `toPromise(...)` to convert any actor, including promise actors, to a promise. This is useful if you want to `await` the `output` of an actor:

```ts
import { toPromise, createActor } from 'xstate';
import { somePromiseLogic } from './somePromiseLogic';

const promiseActor = createActor(somePromiseLogic);
promiseActor.start();

// [!code highlight:1]
const output = await toPromise(promiseActor);

console.log(output);
// logs the resolved output of the promise actor
```

## Promise actor error handling

If an error occurs in the promise logic, the promise actor will reject with the error. You can subscribe to the promise actor and check the `status` property of the snapshot in an **error observer** (the `error` property of the observer object).

```ts
import { fromPromise } from 'xstate';

const promiseLogic = fromPromise(async () => {
  // ...
  throw new Error('Something went wrong');
});

const promiseActor = createActor(promiseLogic);
promiseActor.subscribe({
  // [!code highlight:4]
  error: (error) => {
    console.error(error);
    // logs 'Error: Something went wrong'
  },
});

promiseActor.start();
```

## Stopping promise actors

You can stop a promise actor created using `createActor(promiseLogic)` by calling `.stop()` on the actor instance. This will discard the resolved or rejected value of the promise and dispose of any subscriptions to the promise actor.

You can also **abort** a promise actor by passing the `signal` to it:

```ts
import { fromPromise, createActor } from 'xstate';

const promiseLogic = fromPromise(
  async ({
    input,
    // [!code highlight:1]
    signal,
  }) => {
    // [!code highlight:2]
    // Pass the signal to abort fetching if signaled
    const data = await fetch('/some/url', { signal });

    return data;
  },
);

const promiseActor = createActor(promiseLogic);
promiseActor.start();

// ...

// [!code highlight:2]
// Abort the promise actor
promiseActor.stop();
```


# Pure transition functions (/docs/pure-transitions)

Pure transition functions allow you to compute the next state and actions of a state machine without creating a live actor or executing any side effects. This is useful for server-side applications, testing, and scenarios where you need to compute state transitions without side effects.

There are two main functions you can use to compute state transitions:

* `initialTransition(machine, input?)`: Returns a tuple of `[initialState, initialActions]` that represents the initial state and any entry actions for a state machine.
* `transition(machine, state, event)`: Returns a tuple of `[nextState, actions]` that represents the next state and any actions that would be executed during the transition.

```ts
import { createMachine, initialTransition, transition } from 'xstate';

const machine = createMachine({
  initial: 'pending',
  states: {
    pending: {
      on: {
        start: { target: 'started' },
      },
    },
    started: {
      entry: { type: 'doSomething' },
    },
  },
});

// [!code highlight:5]
// Get initial state and actions
const [initialState, initialActions] = initialTransition(machine);

console.log(initialState.value); // 'pending'
console.log(initialActions); // [{ type: 'doSomething', ... }]

// [!code highlight:7]
// Get next state and actions
const [nextState, actions] = transition(machine, initialState, {
  type: 'start', // The event to send
});

console.log(nextState.value); // 'started'
console.log(actions); // [{ type: 'doSomething', ... }]
```

This pure functional approach offers several benefits:

* **Deterministic**: Same input always produces the same output
* **Testable**: Easy to test state logic without managing actor lifecycles
* **Server-friendly**: Perfect for server-side workflows and API endpoints
* **Debuggable**: Can inspect state changes and actions without side effects

## `initialTransition(machine, input?)`

Returns the initial state and any entry actions for a state machine. If the machine requires `input`, you should pass it as the second argument to `initialTransition`.

```ts
import { createMachine, initialTransition, transition } from 'xstate';

const machine = createMachine({
  initial: 'pending',
  context: ({ input }) => ({
    count: input.initialCount,
  }),
  states: {
    pending: {
      on: {
        start: { target: 'started' },
      },
    },
    started: {
      entry: { type: 'doSomething' },
    },
  },
});

// [!code highlight:8]
// Get initial state and actions
const [initialState, initialActions] = initialTransition(machine, {
  initialCount: 0,
});

console.log(initialState.value); // 'pending'
console.log(initialState.context); // { count: 0 }
console.log(initialActions); // [{ type: 'doSomething', ... }]
```

### `transition(machine, state, event)`

Computes the next state and actions given a current state and event.

```ts
import { createMachine, initialTransition, transition } from 'xstate';

const machine = createMachine({
  initial: 'pending',
  states: {
    pending: {
      on: {
        start: { target: 'started' },
      },
    },
    started: {
      entry: { type: 'doSomething' },
    },
  },
});

// Get initial state and actions
const [initialState, initialActions] = initialTransition(machine);

// [!code highlight:7]
// Get next state and actions
const [nextState, actions] = transition(machine, initialState, {
  type: 'start', // The event to send
});

console.log(nextState.value); // 'started'
console.log(actions); // [{ type: 'doSomething', ... }]
```

## Actions

Actions represent side effects that would be executed during a transition. The pure functions capture these actions but don't execute them, giving you full control over when and how to handle them.

The primary focus should be on **custom actions** - actions you define in your state machine. These are captured as action objects with `type` and `params`:

```ts
import { createMachine, setup, transition } from 'xstate';

const machine = setup({
  actions: {
    sendEmail: (_, params: { to: string; subject: string }) => {
      // This won't execute in pure functions
      console.log(`Sending email to ${params.to}: ${params.subject}`);
    },
    updateDatabase: (_, params: { userId: string; data: any }) => {
      // This won't execute in pure functions
      console.log(`Updating user ${params.userId}`, params.data);
    },
  },
}).createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        processUser: {
          target: 'processing',
          actions: [
            {
              type: 'sendEmail',
              params: ({ event }) => ({
                to: event.email,
                subject: 'Processing started',
              }),
            },
            {
              type: 'updateDatabase',
              params: ({ event }) => ({
                userId: event.userId,
                data: { status: 'processing' },
              }),
            },
          ],
        },
      },
    },
    processing: {},
  },
});

const [initialState] = initialTransition(machine);
const [nextState, actions] = transition(machine, initialState, {
  type: 'processUser',
  userId: '123',
  email: 'user@example.com',
});

console.log(actions);
// [
//   {
//     type: 'sendEmail',
//     params: { to: 'user@example.com', subject: 'Processing started' }
//   },
//   {
//     type: 'updateDatabase',
//     params: { userId: '123', data: { status: 'processing' } }
//   }
// ]
```

## Resolving Persisted State

When working with persisted state, use `machine.resolveState()` to restore snapshots:

```ts
// Persist state
const stateToPersist = JSON.stringify(currentState);

// Later, restore state
const restoredState = machine.resolveState(JSON.parse(stateToPersist));
const [nextState, actions] = transition(machine, restoredState, event);
```


# Quick start (/docs/quick-start)

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

This quick start guide will help you get started with XState and Stately Studio. You will learn how to create a state machine, create an actor from that state machine, send events to that actor, and observe the state changes.

<Callout>
  The fastest way to start with Stately Studio is to go to [state.new](https://state.new). There, you’ll find a starter machine with all the statechart basics ready for you to edit. [Read more about Stately Studio](studio).
</Callout>

## Installing XState v5

[XState](xstate) is a visual state management and orchestration library for JavaScript and TypeScript.

Install the XState v5 using your preferred package manager:

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate
    ```
  </Tab>
</Tabs>

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

## Create a machine

In XState, a [machine](machines) is an object that contains all the logic for an [actor](actors). In this example, we will create a simple toggle machine that can be in one of two states: `Active` or `Inactive`. The `toggle` event will toggle the state between `Active` and `Inactive`.

```ts
import { createMachine } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'Inactive',
  states: {
    Inactive: {
      on: { toggle: 'Active' },
    },
    Active: {
      on: { toggle: 'Inactive' },
    },
  },
});
```

[Read our introduction to state machines and statecharts](state-machines-and-statecharts) to familiarize yourself with the concepts.

## Create an actor and send events

[Machine logic](/docs/actors#actor-logic) can be used to create an actor. An [actor](actors) is a running process that can receive messages (events), send messages and change its behavior based on the messages it receives.

```js
import { createMachine, createActor } from 'xstate';

const toggleMachine = createMachine({
  // Machine code from above
});

// Create an actor that you can send events to.
// Note: the actor is not started yet!
const actor = createActor(toggleMachine);

// Subscribe to snapshots (emitted state changes) from the actor
actor.subscribe((snapshot) => {
  console.log('Value:', snapshot.value);
});

// Start the actor
actor.start(); // logs 'Inactive'

// Send events
actor.send({ type: 'toggle' }); // logs 'Active'
actor.send({ type: 'toggle' }); // logs 'Inactive'
```

## Use delayed transitions

[Delayed transitions](delayed-transitions) are transitions that automatically happen after a specified interval of time.

```js
export const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'Inactive',
  states: {
    Inactive: {
      on: { toggle: 'Active' },
    },
    Active: {
      on: { toggle: 'Inactive' },
      after: { 2000: 'Inactive' },
    },
  },
});
```

## Handle context data

[Context](context) is how you store data in a state machine actor.

```ts
import { assign, createMachine } from 'xstate';

export const toggleMachine = createMachine({
  id: 'toggle',
  // [!code highlight:1]
  context: { count: 0 },
  initial: 'Inactive',
  states: {
    Inactive: {
      on: { toggle: 'Active' },
    },
    Active: {
      entry: assign({
        count: ({ context }) => context.count + 1,
      }),
      on: { toggle: 'Inactive' },
      after: { 2000: 'Inactive' },
    },
  },
});
```

## Add input

[Input](input) is how initial data can be provided to a machine actor.

[Guards](guards) are used to conditionally allow or disallow transitions.

```ts
import { assign, createMachine } from 'xstate';

export const toggleMachine = createMachine({
  id: 'toggle',
  // [!code highlight:1]
  context: ({ input }) => ({
    count: 0,
    // [!code highlight:1]
    maxCount: input.maxCount,
  }),
  initial: 'Inactive',
  states: {
    Inactive: {
      on: {
        toggle: {
          // Only trigger toggle transition if count is less than maxCount
          // [!code highlight:1]
          guard: ({ context }) => context.count < context.maxCount,
          target: 'Active',
        },
      },
    },
    Active: {
      entry: assign({
        count: ({ context }) => context.count + 1,
      }),
      on: { toggle: 'Inactive' },
      after: { 2000: 'Inactive' },
    },
  },
});

const actor = createActor(toggleMachine, {
  // [!code highlight:1]
  input: { maxCount: 10 },
});

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

actor.start();

actor.send({ type: 'toggle' });
```

## Use your machine with a framework

* [Check out all our packages](/docs/category/xstate-packages)
* [Find out more about the `@xstate/react` package](xstate-react)

```jsx
import { useMachine } from '@xstate/react';
import { toggleMachine } from './toggleMachine';

const App = () => {
  const [state, send] = useMachine(toggleMachine);

  return (
    <div>
      <div>Value: {state.value}</div>
      <button onClick={() => send({ type: 'toggle' })}>Toggle</button>
    </div>
  );
};
```

* [Use the XState v5 templates](https://github.com/statelyai/xstate/tree/main/templates)
* [Find more examples](examples)
* [Migrate from XState V4 to XState V5](migration)


# Setup (/docs/setup)

In XState version 5, you can now use the `setup({ ... })` function to setup types and sources for your machines. This has many benefits:

* Reduced boilerplate for strongly typing and providing named sources
* More robust machine logic, as named sources are guaranteed to exist
* Better type inference for actions, actors, guards, delays, context, events, etc.
* Strongly-typed snapshot and done events for actors
* Strongly-typed state values
* Reusability of source logic

Example usage:

```ts
import { setup, assign } from 'xstate';

const machine = setup({
  types: {
    context: {} as { count: number },
    events: {} as { type: 'inc' } | { type: 'dec' },
  },
  actions: {
    increment: assign({
      count: ({ context }) => context.count + 1,
    }),
    decrement: assign({
      count: ({ context }) => context.count - 1,
    }),
  },
}).createMachine({
  context: { count: 0 },
  on: {
    inc: { actions: 'increment' },
    dec: { actions: 'decrement' },
  },
});
```

<Callout type="warning">
  Ensure you are using the latest version of TypeScript (version 5.0 or higher). See the docs on [TypeScript usage with XState](./typescript) for more information.
</Callout>

## Setting up types

Machine types should be setup in the `types` property of `setup({ types })`. This is where you can setup the types for your machine, including:

* Types for `context`
* Types for `events`, including event payloads
* Types for `input`
* Types for `actions`, including action `params`
* Types for `guards`, including guard `params`
* Types for `actors`

## Migrating from `createMachine`

Migrating from bare `createMachine({ ... })` to `setup({ ... }).createMachine({ ... })` to create a machine is simple.

1. Import `setup` instead of `createMachine` from `'xstate'`
2. Move `types` from `createMachine(...)` to `setup(...)`
3. Move action, actor, guard, etc. sources from the 2nd argument of `createMachine(config, sources)` to `setup({ ... })`

```ts
import {
// createMachine
  setup
} from 'xstate';

const machine =
  setup({
    types: { ... },
    actions: { ... },
    guards: { ... }
  })
  .createMachine({
    // types: { ... }
  }, /* { actions, guards, ... } */);
```


# Sign up for Stately Studio (/docs/sign-up)

[Sign up for a Stately Studio account from the sign up page](https://stately.ai/registry/login) or using the **Sign in** button in [the editor](https://stately.ai/editor)’s top bar.

Sign up using your email address and password, or sign in using your GitHub, Google, or Twitter account credentials.

<Callout>
  We offer a **free trial** on the Stately Studio [Pro](studio-pro-plan), [Team](studio-team-plan), or [Enterprise](studio-enterprise-plan) plans so you can explore how our premium features work for you and your team. You can [upgrade](/upgrade) when you’re signed into Stately Studio.
</Callout>

### Using email

You can now sign up for a Stately Studio account with an email address and password.

#### Forgotten password

If you forget, or want to change your password, use the Forgot password link on the [sign in page](https://stately.ai/registry/login).

### Using social sign in

You can also [sign in to Stately Studio using your GitHub, Google, or Twitter account credentials](https://stately.ai/registry/login). As you have already signed up to your platform of choice, you can skip the sign up with Stately Studio and immediately sign in.

When you sign in with GitHub, Google, or Twitter, we will send emails with information about your account, as well as team and project invitations, to the email address you used to sign up for that platform.


# Simulate mode (/docs/simulate-mode)

import { Play, ListOrderedIcon, RefreshCcw, AlertCircle } from 'lucide-react';

You can simulate your machine as if it were live using the <Play size={18} /> **Simulate** button. Your machine will start in the initial state, and any possible events that can be triggered are highlighted in blue.

<EmbedMachine name="Simulate mode" embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=e5fbd908-42af-440f-8d11-91ca16a4fbda&mode=Simulate" />

## Simulate events

Press any available event to walk through your machine on the canvas or using the event buttons in the <ListOrderedIcon size={12} /> **Simulation controls** panel. Use <RefreshCcw size={12} /> **Reset** in the canvas tools to return to the initial state and clear the [event log](#event-log).

<Callout type="warning">
  Simulate mode is disabled if your machine contains errors. States and events with errors are marked with an <AlertCircle size={12} /> error icon in **Design** mode. Hover over any error icon to find out more about the error. Fix all the errors to enable Simulate mode.
</Callout>

### Simulate eventless (always) events

[Eventless (always) events](/docs/editor-states-and-transitions/#eventless-always-events) will be triggered instantly upon entering their source state unless they’re guarded with a condition.

### Simulate delayed (after) events

[Delayed (after) events](/docs/editor-states-and-transitions/#delayed-after-events) will be taken automatically after completing the timer.

## Event log

All triggered events are recorded, in order, in the <ListOrderedIcon size={12} /> **Simulation controls** panel, along with their target state. Press any event in the log panel list to return to that event. Press any state in the log panel list to return to that state.

Use <RefreshCcw size={12} /> **Reset** to return to the initial state and clear the event log.

<Callout>
  [Live simulation mode](live-simulation) ([premium feature](studio-pro-plan)) helps you share your simulated machine with your team without screen sharing or screenshots.
</Callout>


# Sources (/docs/sources)

import { Code, PenSquare, TextQuote } from 'lucide-react';

You can add source code for [actors](actors), [actions](actions), and [guards](guards) from the <Code size={18} /> **Sources** panel. Your source code will be included in your [live actors](stately-sky-getting-started), [synced projects](import-from-github), and [exported code](export-as-code) found in the <Code size={18} /> **Code** panel.

<Callout>
  Sources for [delayed (after) transitions](delayed-transitions) are not yet supported. You can still use delayed transitions in your machines, but the delay implementation will need to be handled in your exported code rather than defined in the Sources panel.
</Callout>

## Adding sources

You can add sources from the <Code size={18} /> **Sources** panel. Use the <PenSquare size={18} /> edit button alongside the action, actor, or guard on the canvas to open its source.

You can also open the **Sources** panel without selecting an action, actor, or guard. By default, the **Sources** panel shows all the sources in the machine, and you can also add new sources without referencing them with an action, actor, or guard. Sources currently referenced in the machine are highlighted in the **Sources** panel. Unused sources are dimmed.

Each source requires a name (ID) and source code. The ID is used to reference the source code. The source code for the actor, action, or guard will be included in the generated code. Sources have distinct names, so multiple actors, actions, or guards can reference them.

### Unnamed actions, actors, and guards

When importing a machine from your codebase, unnamed actions, actors, and guards are assigned a default name based on its position in your machine config. For example, the following action:

```js
createMachine({
  states: {
    a: {
      states: {
        b: { entry: [assign({ name: 'laura' })] },
      },
    },
  },
});
```

Would be given a default name of `inline:(machine).a.b#entry[0]`. These default names are not visible in the editor but will be used in the synced or exported code unless you give your action, actor, or guard a name. `inline:` is prepended to the name to make them more readable and easier to find in your code. After first importing your machine, we recommend you give all your actions, actors, and guards a name. Naming is a one-time step, and you can always rename them later.

## Deleting sources

You can only delete unused sources that any actors, actions, or guards do not reference. This prevents you from accidentally deleting a currently used source in your machine.

<Callout>
  You can **delete all unused sources** from the **...** menu in the top right corner of the **Sources** panel.
</Callout>

## Source templates

Each source type has optional logic templates. You can use the template buttons to quickly add the implementation code for XState’s [logic creators](/docs/actors#actor-logic-creators), [higher-level guards](/docs/guards#higher-level-guards), and [built-in actions](/docs/actions), and the correct XState imports will be added to your code for sync or export.

## Formatting

You can format your source code with [Prettier](https://prettier.io/) by using the <TextQuote size={18} /> format button.


# Spawn (/docs/spawn)

Sometimes invoking [actors](actors) may not be flexible enough for your needs. For example, you might want to:

* Invoke child machines that last across *several* states
* Invoke a *dynamic number* of actors

You can do this by **spawning** an actor instead of invoking. Actors created by spawning are **spawning actors**, and actors created with invoke are **invoking actors**.

<Callout>
  [Read about the difference between spawning and invoking actors](actors.mdx#invoking-and-spawning-actors).
</Callout>

There are two ways to spawn: the `spawnChild` action creator, or the `spawn` helper function for `assign`.

In most cases, prefer `spawnChild`, which causes an actor to be spawned, and can accept a configurable ID for the actor to reference it later:

```ts
createMachine({
  entry: spawnChild(childMachine, {
    id: 'child',
  }),
});
```

You can use `spawnChild` for multiple spawned actors:

```ts
createMachine({
  entry: [
    spawnChild(childMachine, { id: 'child-1' }),
    spawnChild(childMachine, { id: 'child-2' }),
    spawnChild(childMachine, { id: 'child-3' }),
  ],
});
```

You can also use the `spawn` helper function provided by the `assign` action creator, which allows you to store a reference to the spawned actor (an `ActorRef`) in the machine's `context`:

```ts
const parentMachine = createMachine({
  entry: [
    assign({
      childMachineRef: ({ spawn }) => spawn(childMachine, { id: 'child' }),
    }),
  ],
});
```

However, if you use `spawn`, **make sure you remove the ActorRef from `context` to prevent memory leaks** when the spawned actor is no longer needed:

```ts
actions: [stopChild('child'), assign({ childMachineRef: undefined })];
```

You can `spawn` as many actors as you need:

```ts
const childMachine = createMachine({
  /* ... */
});

const parentMachine = createMachine({
  entry: [
    assign({
      childMachineRefs: ({ spawn }) => [
        // [!code highlight:3]
        spawn(childMachine),
        spawn(childMachine),
        spawn(childMachine),
      ],
    }),
  ],
});
```

If you don't need to keep track of a reference to the spawned actor (e.g.: for anonymous spawned actors), you can use the `spawnChild` action creator. It does *not* return a reference, but indicates to the XState interpreter that a new actor should be spawned:

```ts
createMachine({
  entry: spawnChild('workflow', {
    id: 'workflow',
  }),
});
```

## API

```ts
actions: assign({
  ref: ({ spawn }) => spawn(fromPromise(...), {
    id: 'some-id',
  })
})
```

* `spawn(actorBehavior, options?)`
  * `actorBehavior` - The behavior of the actor to spawn. This can be a function, promise, observable, or callback.
  * `options` - Options for spawning the actor.
    * `id` (optional) - The ID of the actor. This is used to reference the actor in the state machine.
    * `input` (optional) - The input to pass to the actor.
    * `systemId` (optional) - A string identifing the actor, unique system-wide.

## Source

* Inline: `spawn(fromPromise(...))`
* Referenced: `spawn('getUser')`
  * `.provide({ actors })`

## Lifecycle

* Created & started when spawned
* Stopped when the machine is stopped
* Can be manually stopped

## Stopping an actor

You can stop a child actor via the "stop child" action. This action is created from the `stopChild(id)` action creator.

```ts
import { setup, stopChild, fromPromise } from 'xstate';

const machine = setup({
  actors: {
    something: fromPromise(async () => {
      // Some actor logic
      return 'Some response';
    }),
  },
}).createMachine({
  context: ({ spawn }) => ({
    something: spawn('something', { id: 'thing' }),
  }),
  // ...
  on: {
    'thing.stop': {
      // [!code highlight:1]
      actions: stopChild('thing'),
    },
    'thing.stopFromContext': {
      // [!code highlight:1]
      actions: stopChild(({ context }) => context.something),
    },
  },
});
```

Stopping a child actor does *not* remove it from `context`. To remove it from context, use the [`assign(...)` action creator](./context):

```ts
import { setup, stopChild } from 'xstate';

const machine = setup({
  // ...
}).createMachine({
  context: ({ spawn }) => ({
    something: spawn('something', { id: 'thing' }),
  }),
  // ...
  on: {
    'thing.stop': {
      actions: [stopChild('thing'), assign({ something: undefined })],
    },
  },
});
```

## Spawn and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

*Coming soon*

## Spawn cheatsheet

### Basic Spawning

```ts
// Spawn a child machine
createMachine({
  entry: spawnChild(childMachine, {
    id: 'child',
  }),
});

// Spawn multiple children
createMachine({
  entry: [
    spawnChild(childMachine, { id: 'child-1' }),
    spawnChild(childMachine, { id: 'child-2' }),
  ],
});
```

### Spawning with Context

```ts
// Store actor reference in context
createMachine({
  entry: assign({
    childRef: ({ spawn }) => spawn(childMachine, { id: 'child' }),
  }),
});

// Spawn multiple actors in context
createMachine({
  entry: assign({
    childRefs: ({ spawn }) => [spawn(childMachine), spawn(childMachine)],
  }),
});
```

### Spawning Different Actor Types

```ts
// Spawn from promise
spawnChild(
  fromPromise(() => Promise.resolve(42)),
  {
    id: 'promiseActor',
  },
);

// Spawn from observable
spawnChild(
  fromObservable(() => interval(1000)),
  {
    id: 'observableActor',
  },
);

// Spawn from named actor
setup({
  actors: {
    fetchData: fromPromise(({ input }) => Promise.resolve(input)),
  },
}).createMachine({
  entry: spawnChild('fetchData', {
    id: 'fetchActor',
    input: 42,
  }),
});
```

### Spawning Options

```ts
spawnChild(actor, {
  id: 'actorId', // Required for referencing
  input: { data: 42 }, // Optional input data
  systemId: 'systemId', // Optional system-wide unique ID
});
```

### Stopping Spawned Actors

```ts
// Stop by ID
actions: stopChild('actorId');

// Stop from context reference
actions: stopChild(({ context }) => context.actorRef);

// Stop and clean up context
actions: [stopChild('actorId'), assign({ actorRef: undefined })];
```

### Dynamic Spawning

```ts
// Dynamic ID based on context
spawnChild(childMachine, {
  id: ({ context }) => context.childId,
});

// Spawn with dynamic input
spawnChild(childMachine, {
  id: 'child',
  input: ({ context }) => context.inputData,
});
```

### Best Practices

1. Always provide an `id` when you need to reference the actor later
2. Clean up actor references from context when stopping them
3. Use `spawnChild` for one-off actors that don't need context references
4. Use `spawn` with `assign` when you need to track actor references
5. Consider using `systemId` for actors that need to be globally unique


# State done events (/docs/state-done-events)

A **state done event** transitions from a parent state when one of its child states reaches its final state. State done events are labeled “onDone.”

State done events are useful when you want to:

* Model sequential flows where one process must complete before moving to the next
* Coordinate between parent and child states in a hierarchical state machine
* Handle cleanup or transition logic after a subprocess completes
* Automatically transition to a new state when all child states are complete

For example, in a coffee machine, you might use an `onDone` event to transition from the "preparation" state to "brewing" only after all preparation steps (grinding beans, heating water, etc.) are complete. This ensures proper sequencing of operations.

State done events also help maintain separation of concerns by allowing child states to focus on their specific tasks while the parent state handles the overall flow coordination.

<Callout>
  Watch our [“What are state done events?” video on YouTube](https://www.youtube.com/watch?v=3laC3gWBLnM\&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ\&index=11) (1m16s).
</Callout>

In the video player above, when the video player reaches the *Stopped* state, the *Opened* state transitions through the *onDone* state done event to the *Closed* state.

## How to add a state done event to a parent state

1. Check the final child state has its state type set to **Final**. If the parent state doesn’t contain a final child state, the state done event type will not be available for transitions from the parent state.
2. Select the parent state.
3. Drag from the handles on the left, right and bottom sides of the selected state, and release to create a connecting transition, event and new state.
4. Select the newly-created event. Then…

### Using the **quick actions** menu

1. Right-click the state to open the **quick actions** menu.
2. Choose **State done event** from the **Event type** options.

### Using the **Transition details** panel

1. Open the **Transition details** panel from the right tool menu.
2. Choose **State done event** from the **Event type** dropdown menu.


# State machine actors (/docs/state-machine-actors)

import { Info } from 'lucide-react';

State machine actors are actors whose logic is represented by a finite state machine or statechart.

<Callout>
  This page is just an overview on using state machines to represent actor logic. Read the documentation on [state machines](./machines) to learn about state machines in depth.
</Callout>

## State machine actor capabilities

|   | Capability     | Notes                                                                                        |
| - | -------------- | -------------------------------------------------------------------------------------------- |
| ✅ | Receive events | State machine actors can receive events directly (`actor.send(event)`) or from other actors. |
| ✅ | Send events    | State machine actors can send events to other actors it has reference to.                    |
| ✅ | Spawn actors   | State machine actors can spawn/invoke actors and have child actors.                          |
| ✅ | Input          | You can provide `input` to state machine actors.                                             |
| ✅ | Output         | State machine actors can produce `output`.                                                   |

## State machine actor logic

You can define state machine actor logic using the `createMachine(...)` actor logic creator, which takes a finite state machine or statechart configuration object as its only argument.

```ts
import { createMachine, createActor } from 'xstate';

const toggleMachine = createMachine({
  initial: 'inactive',
  states: {
    inactive: {
      on: {
        toggle: {
          target: 'active',
        },
      },
    },
    active: {
      on: {
        toggle: {
          target: 'inactive',
        },
      },
    },
  },
});

const toggleActor = createActor(toggleMachine);
toggleActor.subscribe((snapshot) => {
  console.log(snapshot.value); // 'inactive' or 'active'
});
toggleActor.start();
// logs 'inactive'

toggleActor.send({ type: 'toggle' });
// logs 'active'

toggleActor.send({ type: 'toggle' });
// logs 'inactive'
```

## State machine actor input

You can pass in `input` to a state machine actor by passing it to the `createActor(...)` function as the `input` property of the second argument. In the state machine (`setup(…).createMachine(…)`), you read the `input` property of the first argument passed to the `context` function:

```ts
import { setup, createActor } from 'xstate';

const feedbackMachine = setup({
  // ...
}).createMachine({
  context: ({ input }) => ({
    rating: input.defaultRating,
  }),
  initial: 'question',
  states: {
    question: {
      /* ... */
    },
    // ...
  },
});

const feedbackActor = createActor(feedbackMachine, {
  // [!code highlight:3]
  input: {
    defaultRating: 3,
  },
});

feedbackActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});

feedbackActor.start();
// logs { rating: 3 }
```

## State machine actor output

Read [state machine output](./output) for more information.


# What are state machines and statecharts? (/docs/state-machines-and-statecharts)

State machines help us model how a process goes from state to state when an event occurs.

State machines are useful in software development because they help us capture all the states, events and transitions between them. Using state machines makes it easier to find impossible states and spot undesirable transitions.

State machines model your application logic. Below is the logic for a video player. When the video is Played, it is opened into fullscreen mode. When the video is stopped, it closes out of fullscreen mode. When the video player is in fullscreen mode, it can be *Playing* or *Paused*.

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=dbcfca1c-075d-4cd6-a865-efcbd7be1544" />

## Benefits of state machines

* **Simple to understand.** State machines are visual and simple to understand. They’re a great way to communicate with your team and stakeholders.
* **Simple to test.** State machines are deterministic, which means they’re simple to test. You can test all the possible states and transitions between them.
* **Simple to implement.** State machines are simple to implement in any programming language. You can use a library or write your own.
* **Simple to maintain.** State machines are simple to maintain because they’re simple to understand, test and implement.
* They provide a clear and concise way to model complex user interfaces and manage application state.
* State machines can help reduce complexity and improve maintainability by providing a structured way to manage state transitions and handle events.
* They can also help prevent bugs and improve code quality by enforcing a clear separation of concerns between different parts of the application.
* Additionally, state machines can be highly maintainable and offer a way to make very complex front-end processes much more manageable.

### In the backend

* State machines simplify the design and implementation of complex workflows, which can help reduce complexity and improve maintainability. [https://developers.redhat.com/articles/2021/11/23/how-design-state-machines-microservices](https://developers.redhat.com/articles/2021/11/23/how-design-state-machines-microservices)

## What is a statechart?

Statecharts extend traditional finite state machines to model more complex logic.

Statecharts are a visual extension to state machines that use boxes and arrows, much like flowcharts and sequence diagrams. Statecharts add extra features not available in ordinary state machines, including [hierarchy](parent-states), [concurrency](parallel-states) and [communication](actor-model).

When you make a state machine in [Stately Studio](https://stately.ai/editor), it’s also a statechart!

<Callout>
  In Stately Studio, we refer to both state machines and statecharts as **state machines**.
</Callout>

## States

A state describes the machine’s status or mode, which could be as simple as *Asleep* and *Awake*. A state machine can only be in one state at a time.

<EmbedMachine name="Dog states" embedURL="https://stately.ai/registry/editor/embed/1f84ff0d-fe41-4a92-ad5c-fadfa8b37ffb?machineId=469f2d59-551f-43cb-bfc0-e6f3ea0f9d87" />

A dog is always **asleep** or **awake**. The dog can’t be asleep and awake at the same time, and it’s impossible for the dog to be neither asleep nor awake. There are only these two states, a precisely limited, *finite* number of states.

When a state machine starts, it enters the **initial state** first. A machine can only have one top-level initial state; if there were multiple initial states, the machine wouldn’t know where to start!

In this statechart describing the process of walking the dog, the initial state would be **waiting** to walk.

* [Read more about states](states).
* [Read more about finite states](finite-states).
* [Read more about initial states](initial-states).

### States in XState

States are defined in XState state machines inside the `states` property.

```ts
import { createMachine } from 'xstate';

const dogMachine = createMachine({
  id: 'dog',
  initial: 'asleep',
  // [!code highlight:9]
  states: {
    asleep: {
      // ...
    },
    awake: {
      // ...
    },
    //...
  },
});
```

<Callout>
  ### States in Stately Studio

  The rounded rectangle boxes are states. [Read how to create states in Stately Studio](states).
</Callout>

## Transitions and events

How the dog goes between **asleep** and **awake** is through **transitions**. Events cause transitions; when an event happens, the machine transitions to the next state. The dog goes between **asleep** and **awake** through the **wake up** and **fall asleep** events.

Transitions are “deterministic”; each combination of state and event always points to the same next state. Dogs never **wake up** to become **asleep** or **fall asleep** to become **awake**.

<EmbedMachine name="Dog states with transitions" embedURL="https://stately.ai/registry/editor/embed/1f84ff0d-fe41-4a92-ad5c-fadfa8b37ffb?machineId=e48e774e-c31f-4e51-a934-6b795c12b2b9" />

With its two finite states and transitions, this tiny dog process is a *Finite State Machine.* A state machine is used to describe the behavior of something. The machine describes the thing’s states and the transitions between those states. It’s a Finite State Machine because it has a finite number of states. (Sometimes abbreviated to FSM by folks who love jargon).

[Read more about transitions and events](transitions).

### Events and transitions in XState

Events and transitions are defined in XState state machines inside the `on` property of a state.

```ts
import { createMachine } from 'xstate';

const dogMachine = createMachine({
  id: 'dog',
  initial: 'asleep',
  states: {
    asleep: {
      // [!code highlight:3]
      on: {
        'wakes up': 'awake',
      },
    },
    awake: {
      // [!code highlight:3]
      on: {
        'falls asleep': 'asleep',
      },
    },
    //...
  },
});
```

<Callout>
  ### Events and transitions in Stately Studio

  In Stately Studio, the arrows are transitions, and the rounded rectangles on the arrow’s lines are events. Each transition has a **source** state which comes before the transition, and a **target** state, which comes after the transition. The transition’s arrow starts from the source state and points to the target state. [Read how to create events and transitions in Stately Studio](editor-states-and-transitions).
</Callout>

## Final state

Most processes with states will have a *final state*, the last state when the process is finished. The final state is represented by a double border on the state’s rounded rectangle box.

In the dog walking statechart, the final state would be **walk complete**.

<EmbedMachine name="Dog walking" embedURL="https://stately.ai/registry/editor/embed/1f84ff0d-fe41-4a92-ad5c-fadfa8b37ffb?machineId=988d8d05-86ba-422a-8a28-d13cbf54d481" />

[Read more about final states](final-states)

## Parent states

A parent state is a state that can contain more states, also known as child states. These child states can only happen when the parent state is happening. Inside the **on a walk** state, there could be the child states of **walking** and **running**.

A parent state is symbolised by a labelled rectangle box that acts as a container for its child states. Parent states are sometimes known as *compound states*.

<EmbedMachine name="Dog walk with parent state" embedURL="https://stately.ai/registry/editor/embed/1f84ff0d-fe41-4a92-ad5c-fadfa8b37ffb?machineId=aa8a9c5d-8fd9-4e47-b71a-bda219cda066" />

A parent state should also specify which child state is the initial state. In the **on a walk** parent state, the initial state is **walking**.

Parent and child states are one of the features that make statecharts capable of handling more complexity than an everyday state machine.

[Read more about parent states](parent-states).

## Atomic states

An atomic state is a state that doesn’t have any child states. **Waiting**, **walk complete**, **walking** and **running** are all atomic states.

## Parallel states

A parallel state is a state where all of its child states, also known as regions, are active simultaneously. The regions are separated inside the parallel state container by a dashed line.

Inside the **on a walk** parallel state, there could be two regions. One region contains the dog’s activity child states of **walking** and **running**, and the other region containing the dog’s tail states of **wagging** and **not wagging**. The dog can walk and wag its tail or run and wag its tail, it can also do both of these activities without wagging its tail.

<EmbedMachine name="Dog walk with parallel state" embedURL="https://stately.ai/registry/editor/embed/1f84ff0d-fe41-4a92-ad5c-fadfa8b37ffb?machineId=1f43dcd3-255c-40bf-b6b0-eba9a2bccb23" />

Both regions should also specify which child state is the initial state. In our **tail** region, the initial state is **not wagging**.

[Read more about parallel states](parallel-states).

## Self-transition

A self-transition is when an event happens, but the transition returns to the same state. The transition arrow exits and re-enters the same state.

A helpful way to describe a self-transition is “doing something, not going somewhere” in the process.

In a **dog begging** process, there would be a **begging** state with a **gets treat** event. And for the dogs who love their food, no matter how many times you go through the **gets treat** event, the dog returns to its **begging** state.

<EmbedMachine name="Dog begging" embedURL="https://stately.ai/registry/editor/embed/17986605-400c-4c00-8b33-47e3ecca478b?machineId=933419cb-dd94-453c-a2b1-de2290c0a5a5" />

[Read more about self transitions](/docs/transitions/#self-transitions)

## What next?

Now you know enough to get started in the [Stately Studio](https://stately.ai/editor?source=docs). Get an [overview of Stately Studio and its features](studio), or [continue to learn about XState](xstate).


# Stately Sky getting started (/docs/stately-sky-getting-started)









# Getting started with Stately Sky 🌤️

This guide will walk you through deploying a simple traffic light state machine workflow with Stately Sky using [XState](/docs/xstate), [Vite](https://vitejs.dev/) and [React](https://reactjs.org/).

<Callout type="warning">
  Please note that Sky is currently in beta and will be changing rapidly.
</Callout>

## What you’ll need

* A [Stately](https://stately.ai) account with a [Pro, Team, or Enterprise subscription](https://stately.ai/pricing).
* Our [Stately Sky starter project](https://github.com/statelyai/sky-starter-app). Clone the repo to your local machine.

<Callout>
  Our premium users get unlimited use of Stately Sky while it’s in beta. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

## Getting started video

<YouTube id="JJHC0UyGsrQ" />

## Step 1: Create a machine with Stately

Create a project and compose your machine in the [Stately editor](https://stately.ai/editor) with the transitions and states you want.

For this example, we’ll create a simple traffic light machine with three states: `green`, `yellow`, and `red`. Feel free to fork [our traffic light example](https://stately.ai/registry/editor/eb3e89f5-5936-439f-8254-2f6ea4303659?machineId=15fd8071-b80c-4a6f-b9f5-60b6cf578ee5) to test. Check out a [deployed version of this traffic light machine](https://sky-starter.stately.ai/?page=trafficlight).

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/eb3e89f5-5936-439f-8254-2f6ea4303659?machineId=15fd8071-b80c-4a6f-b9f5-60b6cf578ee5" name="Traffic light" />

<Callout>
  Sky only supports [XState](https://github.com/statelyai/xstate) V5 machines. The [changes in XState V5](/docs/migration) provide both a better developer experience and adhere to the [Actor Model](/docs/actor-model) more closely, allowing Sky to capably deploy machines that reliably communicate their state.
</Callout>

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

## Step 2: Create an API key

After creating your machine, you’ll need to create an API key to deploy it to Sky.

1. Use the **Deploy** button in the top right corner of the editor to open the Stately Sky options.
2. Use the **Create API Key** button to generate an API key.

<img alt="Stately Sky modal showing no API key created yet. There is a button to Create API Key." src={__img0} placeholder="blur" />

3. Be sure to copy that API key and save it somewhere safe. You’ll need it later.

The page should look like this:

<img alt="Stately Sky modal shows the API key in an input hidden behind password dots. There are options to Regenerate, Delete, Reveal, or copy the API key to the clipboard." src={__img1} placeholder="blur" />

## Step 3: Deploy your machine to Sky

Once you have generated the API key, you can deploy your machine to Sky as a workflow.

1. Use the **Deploy to Sky 🌤️** button to start the deployment process.
2. When the workflow is deployed, it will be listed under **Existing deploys**.
3. Use **Copy URL** to copy to the URL to your clipboard.

You’ll need the workflow’s URL to reference it from the starter project.

<img alt="Stately Sky modal shows the Traffic light workflow listed under Existing deploys. The workflow has its own URL with the options to Copy URL or delete alongside." src={__img2} placeholder="blur" />

## Step 4: Add the actor in the starter project

After adding the API key, you’ll need to create an actor.

1. Create a new file in the `src` directory of the starter project. We named ours `trafficLightActor.ts`.
2. In your new file, import the `actorFromStately` function and initialize the actor with the provided URL and your own session ID:

```typescript
import { actorFromStately } from '@statelyai/sky';

const actor = actorFromStately({
  apiKey: 'paste your API key here',
  url: 'paste your Sky url here',
  sessionId: 'your session id here',
});
```

<Callout>
  By default, Sky is multiplayer.
  The session ID is used to shard the multiplayer session. Each actor has a unique session ID.

  * Use a shared session ID to allow multiple users to reference the same actor.
  * If you want users isolated, use a unique session ID for each user.
</Callout>

## Step 5: Fetching the config from Sky

Now that we’ve created the actor, we need to fetch the config from Sky. Doing so will download and generate the machine configuration file in our repo, giving us type safety when interacting with the running actor!

To fetch the config, we’ll use the [XState CLI tool](/docs/developer-tools.mdx#xstate-cli-command-line-interface) and the `sky` script already in our `package.json`. This script runs the command over all the files in the `src` repo to find configs associated with any initialized actors.

<img alt="package.json" src={__img3} placeholder="blur" />

1. Using your package manager of choice, run the `sky` command:

```bash npm2yarn
npm run sky
```

2. Once the `sky` command has completed, you should see:

* a second `skyConfig` argument with updated imports passed to the `actorFromStately` function.
* a new TypeScript file in your `src` directory, named after the actor in the Studio. In our case, it’s `trafficLightActor.sky.ts`.

You’ll notice a warning in the `sky.ts` file that the file is generated. You should not manually edit these files as any local changes will not reflect what’s running in Sky.

Running `xstate sky` will only affect a file if it hasn’t already been fetched. If you make changes to the machine in the Studio, you’ll need to delete the generated file `yourFile.sky.ts` and run the command again. Alternatively, you can force the refetch by running `xstate sky --refetch`.

<Callout>
  Add your generated `sky.ts` files to source control.
</Callout>

## Finishing up

And that’s it! You can now interact with your running actor in much the same way you would with local actors, like sending events with the `send()` function. Sky is still in its early days, so there are some limitations and things to remember. Specifically:

* Only XState V5 machines are supported.
* If you're using React Native, see [this comment](https://github.com/statelyai/sky/issues/6#issuecomment-1766967479) if you're having issues.


# State (/docs/states)

A state describes the machine’s status or mode, which could be as simple as *Paused* and *Playing*. A state machine can only be in one state at a time.

These states are “finite”; the machine can only move through the states you’ve pre-defined.

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=741f69fd-7f01-4932-9407-6871e225bb6d" />

<Callout>
  You can visualize your state machines and easily add states in our drag-and-drop Stately editor. [Read more about states in Stately’s editor](editor-states-and-transitions).
</Callout>

<Callout>
  Watch our [“What are states?” video on YouTube](https://www.youtube.com/watch?v=z-6yhmSWUcc\&list=PLvWgkXBB3dd4I_l-djWVU2UGPyBgKfnTQ\&index=2) (53s).
</Callout>

## State object

The state object represents the current state of a running machine ([actor](actor-model)) and contains the following properties:

* **`value`**: the current state value, which is either:
  * a string representing a simple state like `'playing'`, or:
  * an object representing nested states like `{ paused: 'buffering' }`.
* **`context`**: the current [context](context) (extended state.)
* **`meta`**: an object containing state node meta data.

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  context: {
    feedback: '',
  },
  states: {
    question: {
      meta: {
        question: 'How was your experience?',
      },
    },
  },
});

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

console.log(actor.getSnapshot());
// Logs an object containing:
// {
//   value: 'question',
//   context: {
//     feedback: ''
//   },
//   meta: {
//     'feedback.question': {
//       question: 'How was your experience?'
//     }
//   }
// }
```

## Accessing state snapshots

You can access an actor’s emitted state (or *snapshot*) by subscribing to or reading from the actor’s `.getSnapshot()` method.

```ts
import { createActor } from 'xstate';

// ...

const actor = createActor(feedbackMachine);

actor.subscribe((snapshot) => {
  console.log(snapshot);
  // logs the current snapshot state, e.g.:
  // { value: 'question', ... }
  // { value: 'thanks', ... }
});

actor.start();

console.log(actor.getSnapshot());
// logs { value: 'question', ... }
```

## State value

A state machine with nested states (or *[statechart](state-machines-and-statecharts.mdx#what-is-a-statechart)*) is a tree-like structure where each node is a *state node*. The root state node is the top-level state node that represents the entire machine. The root node may have child state nodes, which may have child state nodes, and so on.

The **state value** is an object that represents all the active state nodes in a machine. For state machines that have state nodes without child state nodes, the state value is a string:

Coming soon… a visual example.

* `state.value === 'question'`
* `state.value === 'thanks'`
* `state.value === 'closed'`

For state machines with parent state nodes, the state value is an object:

Coming soon… a visual example.

* `state.value === { form: 'invalid' }` - this represents a state machine with an active child node with key `form` that has an active child node with key `invalid`

For state machines with [parallel state nodes](parallel-states), the state value contains object(s) with multiple keys for each state node region:

```ts
state.value ===
  {
    monitor: 'on',
    mode: 'dark',
  };
```

State machines may also have no state nodes other than the root state node. For these state machines, the state value is `null`.

## State context

State machines can have [context](context), which is an object that represents the extended state of the machine. The context is immutable, and can only be updated by [assigning](actions.mdx#assign-action) to it in an action. You can read the `state.context` property to get the current context.

```ts
const currentState = feedbackActor.getSnapshot();

console.log(currentState.context);
// logs { feedback: '' }
```

* Object is empty `{}` (default) if context is not specified in the machine config
* Never mutate this object; should be treated as immutable/read-only

## State children

The `state.children` property represents all currently spawned/invoked actors in the current state. It is an object with keys representing the actor IDs and values representing the `ActorRef` instances.

* This is where you access spawned/invoked actors by their ID
* This is why you should give spawned/invoked actors IDs
* Stopped actors will not appear here

## `state.can(eventType)`

The `state.can(event)` method determines whether an `event` object will cause a state change if sent to the machine actor. The method will return `true` if the state will change due to the `event` being sent; otherwise the method will return `false`:

```js
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    form: {
      // ...
      on: {
        'feedback.submit': {
          guard: 'isValid',
          target: 'thanks',
        },
      },
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

// ...

const currentState = feedbackActor.getSnapshot();

console.log(currentState.can({ type: 'feedback.submit' }));
// logs `true` if the 'feedback.submit' event will cause a transition, which will occur if:
// - the current state is 'form'
// - the 'isValid' guard evaluates to `true`
```

A state is considered "changed" if a transition is enabled for the given `state` and `event` object.

<Callout>
  The `state.can(...)` method will also check transition guards by executing them. Transition guards should be pure functions.
</Callout>

## `state.hasTag(tag)`

The `state.hasTag(tag)` method determines whether any state nodes in the current state value have the given `tag`. This is useful for determining whether a state is a particular state, or whether a state is a member of a particular state group.

```js
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    submitting: {
      tags: ['loading'],
      // ...
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

const currentState = feedbackActor.getSnapshot();

const showLoadingSpinner = currentState.hasTag('loading');
```

Prefer using `state.hasTag(tag)` over `state.matches(stateValue)`, as `state.hasTag(tag)` is more resilient to changes in the machine.

## `state.matches(stateValue)`

The `state.matches(stateValue)` method determines whether the current `state.value` *matches* the given `stateValue`. If the current `state.value` is a "subset" of the provided `stateValue`, then the method will return `true`.

```ts
// state.value === 'question'
state.matches('question'); // true

// state.value === { form: 'invalid' }
state.matches('form'); // true
state.matches('question'); // false
state.matches({ form: 'invalid' }); // true
state.matches({ form: 'valid' }); // false

// state.value === { 'form submitting' : 'invalid value' }
state.matches('form submitting'); // true
state.matches('form'); // false
state.matches({ 'form submitting': 'invalid value' }); // true
state.matches({ 'form submitting': 'value' }); // false
```

## `state.output`

The `state.output` property represents the output data of a state machine when it is in its top-level final state; i.e., `state.status === 'done'`.

```ts
const state = actor.getSnapshot();

if (state.status === 'done') {
  console.log(state.output);
}
```

## `state.getMeta()`

The `state.getMeta()` method represents the metadata of all the state nodes in the `state`. It is an object with keys that represent the state node IDs, and values that are the metadata of that state node.

```ts
import { createMachine, createActor } from 'xstate';

const feedbackMachine = createMachine({
  id: 'feedback',
  // ...
  states: {
    form: {
      meta: {
        view: 'shortForm',
      },
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

// Assume the current state is 'form'
const currentState = feedbackActor.getSnapshot();
console.log(currentState.getMeta());
// logs { 'feedback.form': { view:'shortForm' } }
```

## State descriptions

You can add `.description` to states to describe their purpose and share related notes with your team. In Stately Studio’s editor, these descriptions are rendered in the machine and support markdown including links, images, and lists. [Read more about descriptions in Stately Studio](descriptions).

```ts
states: {
  "Loading Move Destinations": {
    // [!code highlight:2]
    description:
      "Load data from the server based on the entity's id and type (project or machine). Result includes the entity's current location, and the list or tree of valid destination options to which the user may move that entity.",
    invoke: {
      src: "loadMoveData",
      id: "loadMoveData",
      onDone: [
        {
          target: "Destination Menu",
          actions: "setDestinations",
        },
      ],
      onError: [
        {
          target: "Data Loading Error",
        },
      ],
    },
  },
}
```

## States and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

*Coming soon*

## States cheatsheet

### Cheatsheet: read or match a state value

```ts
const state = actor.getSnapshot();

const currentStateValue = state.value;

const isLoading = state.matches('loading');
```

### Cheatsheet: read state context

```ts
console.log(state.context);
```

### Cheatsheet: read state output

```ts
if (state.status === 'done') {
  // Output may exist
  console.log(state.output);
} else {
  // Output does not exist
  state.output === undefined;
}
```

### Cheatsheet: read state meta data

```ts
console.log(state.getMeta());
```

## Further resources

[Persisting state](persistence)


# Stately Studio API (/docs/studio-api)







You can use the Studio's REST API to access your projects and machines in any of your external projects. This allows you to automate your workflow, integrate your machines with other projects, and more.

Some things you can do with the API include:

* Exporting machines to your desired language/format (e.g: TypeScript, JavaScript, JSON, etc.)
* Fetching information about your desired machines
* Fetching information about your projects
* Fetching a specific version of a machine

<Callout>
  API access is a premium feature of Stately Studio. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

## Authentication: Generating an API key

All requests to the Studio must be authenticated with an API key.
You can generate an API key for one of two scopes:

* **Project**: This key will have access to all the machines in a specific project.
* **Account**: This key will have access to all the projects and machines in your account.

<Callout>
  It's important to keep your API key secret. If you believe your API key has been compromised, you can regenerate it at any time.
</Callout>

### Generating a project-wide API key

To generate an API key for a **project**, open any machine in the project you're interested in. Then, click on the "Deploy" button in the top right corner of the editor page. In the modal that appears, click "Create API Key", and make sure to copy it and store it in a safe place.

{/* <p>
  <ThemedImage
    alt="A GIF of the Studio's deploy modal, with the 'Create API Key' button highlighted."
    sources={{
      light: '/generate-project-token-light.gif',
      dark: '/generate-project-token-dark.gif',
    }}
  />

  </p> */}

<img alt="A GIF of the Studio's deploy modal, with the 'Create API Key' button highlighted." src={__img0} />

### Generating an account-wide API key

Generating an API key for an **account** can be done from the user's settings. In Settings, select the API Key tab, and click "Create API Key". Make sure to copy it and store it in a safe place.

{/* <p>
  <ThemedImage
    alt=" A GIF of the Studio's settings page, with the 'Create API Key' button highlighted."
    sources={{
      light: '/generate-account-token-light.gif',
      dark: '/generate-account-token-dark.gif',
    }}
  />

  </p> */}

<img alt="A GIF of the Studio's settings page, with the 'Create API Key' button highlighted." src={__img1} />

## Making requests: Accessing the OpenAPI(Swagger) documentation

The easiest way to get started with the Studio API is to use the [OpenAPI(Swagger) documentation](https://stately.ai/registry/openapi). This documentation provides a list of all the available endpoints, their parameters, and the expected responses.

### Authorizing requests

To test the API, you can must first authenticate with your API key, using the "Authorize" button in the top right corner of the page.

{/* <p>
  <ThemedImage
    alt="A GIF of the Studio's OpenAPI documentation, with the 'Authorize' button highlighted."
    sources={{
      light: '/swagger-auth.gif',
      dark: '/swagger-auth.gif',
    }}
  />

  </p> */}

<img alt="A GIF of the Studio's OpenAPI documentation, with the 'Authorize' button highlighted." src={__img2} />

### Testing endpoints

Once authenticated, you can test any of the endpoints by clicking on them and filling in the required parameters. The parameters can all be easily accessed from the url of the machine, machineVersion, or project in question. Simply open a machine or project in the Studio, and copy the id's from the url in your browser's address bar.
Then, use the "Try it out" button to make a request to the endpoint. The response will be displayed below the request parameters.


# Stately Studio Community plan (/docs/studio-community-plan)

**Stately Studio will always be free to our Community users** on this free plan, and we will make many future features available on every plan. Request features and check out what we’ve got planned on [our roadmap](https://feedback.stately.ai).

<Callout>
  We offer a **free trial** on the Stately Studio [Pro](studio-pro-plan), [Team](studio-team-plan), or [Enterprise](studio-enterprise-plan) plans so you can explore how our premium features work for you and your team. You can [upgrade](/upgrade) when you’re signed into Stately Studio.
</Callout>

## Community features

Our Community plan features include the following:

* Unlimited [public projects](projects.mdx#change-a-projects-visibility)
* [Design and simulate flows](/studio.mdx#studio-editor)
* [Import from code](import-from-code)
* [Export as code](export-as-code)
* [Discover and fork public machines](discover)
* [XState VS Code extension](xstate-vscode-extension)

<Callout>
  ## Why pay for a premium plan?

  Our premium plans include private and unlisted projects, AI assistance with Stately AI, live workflows with Stately Sky, useful features for team management and much more. Your subscription also helps support the Stately team as we build and improve Stately Studio and XState.
</Callout>


# Stately Studio Enterprise plan (/docs/studio-enterprise-plan)

import { Mail } from 'lucide-react';

Upgrade to an Enterprise account to enjoy all of our paid plan features as well as features designed to integrate Stately Studio with the specific needs of your organization, including custom integrations, greater security, and top-tier support. An enterprise account includes the following features:

## Enterprise features

* Everything from the [Team](studio-team-plan) plan
* Unlimited [generated flows](generate-flow)
* Flexible hosting
* Dedicated priority support
* Custom server locations
* Single sign-on (SSO)
* Audit logs
* Embed Stately in your own apps
* Custom effect collections (actions, actors, and more)
* Prioritized feature requests
* A custom plan tailored to the requirements of your organization

We have many more team features coming soon. Request features and check out what we’ve got planned on [our roadmap](https://feedback.stately.ai).

<a href="mailto:support@stately.ai?subject=I'm interested in Stately Studio Enterprise plan">
  <Mail size={18} /> Email the Stately team
</a>

{' '}

for a custom plan tailored to the requirements of your organization.

<Callout title="Why pay for a premium plan?">
  Our premium plans include private and unlisted projects, AI assistance with Stately AI, live workflows with Stately Sky, useful features for team management and much more. Your subscription also helps support the Stately team as we build and improve Stately Studio and XState.
</Callout>


# Stately Studio Pro plan (/docs/studio-pro-plan)

[Upgrade to a Pro account](https://stately.ai/pricing) to create more expressive flows, work faster with AI assistance, and achieve greater flexibility in editing with version control. A Pro account includes the following features:

## Pro features

* Everything from the [Community plan](studio-community-plan)
* [Private and unlisted projects](/docs/projects/#change-a-projects-visibility)
* [Generate and modify using AI](generate-flow)
* [Deploy live workflows](stately-sky-getting-started)
* [Generate React UIs](generate-react)
* [Version history](versions)
* [Import from GitHub](import-from-github)
* [Live simulation mode](live-simulation)
* [Embed Figma frames](figma)
* [State assets](assets)
* [Lock machines](lock-machines)
* [Generate test paths](generate-test-paths)
* [Color states and transitions](colors)
* [Export flows to markdown](export-as-code)
* [Export flows as stories](export-as-code)
* [Priority support](/docs/studio-pro-plan/#priority-support)
* [GitHub Sync](import-from-github)
* Actors (coming soon!)

We have many more pro features coming soon. Request features and check out what we’ve got planned on [our roadmap](https://feedback.stately.ai).

We offer a **free trial** on all of our premium plans so you can explore how our Pro features work for you.

<Callout>
  **Stately Studio will always be free to our Community users** on the free plan, and we will make many future features available on every plan.
</Callout>

## Pro accounts

You can choose between monthly or yearly billing on all of our premium plans.

<Callout title="Pro features as a team member">
  If you are a team member of another user’s [team](teams), with the role of [Admin](/docs/teams/#admin-role) or [Editor](/docs/teams/#editor-role), you also have access to [premium features](#pro-features)! However, if you want to create your own teams, you need to [upgrade to your own Team subscription](upgrade).
</Callout>

### How to sign up

[Sign up for a Stately Studio account from the sign up page](https://stately.ai/registry/login) or using the **Sign in** button in [the editor](https://stately.ai/editor)’s top bar.

Read more about [signing up for Stately Studio](sign-up).

### How to upgrade

You can [upgrade](upgrade) when you’re signed into Stately Studio using the **Upgrade** button in the editor’s footer.

## Priority support

When you sign up for a Pro plan, you will receive an email with information on accessing priority support from the Stately team.

## Canceling your Pro plan

You can cancel your Pro plan at any time. When you cancel your Pro plan:

* New projects will be public by default.
* Private projects will become read-only.
* You will no longer have access to other Pro features like deploying live workflows with Stately Sky, AI generation features, saving machine versions, etc.

<Callout title="Why pay for a premium plan?">
  Our premium plans include private and unlisted projects, AI assistance with Stately AI, live workflows with Stately Sky, useful features for team management and much more. Your subscription also helps support the Stately team as we build and improve Stately Studio and XState.
</Callout>


# Stately Studio Team plan (/docs/studio-team-plan)

[Upgrade to a Team account](https://stately.ai/pricing) to enjoy all of our Pro features as well as many features designed to help you collaborate with your team, on any number of projects. A Team account includes the following features:

## Team features

* Everything from the [Pro](studio-pro-plan) plan
* Create one or more [teams](teams), based on your needs
* [Private, shared team projects](/docs/projects/#change-a-projects-visibility)
* Add up to 10 [team members](/docs/teams/#invite-new-members-to-a-team)
* [Team admins and editors](/docs/teams/#admin-role)
* Grant [view-only access](/docs/teams/#viewer-role) to team projects
* Live collaboration (coming soon!)

We have many more team features coming soon. Request features and check out what we’ve got planned on [our roadmap](https://feedback.stately.ai).

We offer a **free trial** on all of our premium plans so you can explore how our Team features work for you and your team.

<Callout>
  **Stately Studio will always be free to our Community users** on the free plan, and we will make many future features available on every plan.
</Callout>

## Team accounts

You can choose between monthly or yearly billing on all of our premium plans.

<Callout title="Pro features as a team member">
  If you are a team member of another user’s [team](teams), with the role of [Admin](/docs/teams/#admin-role) or [Editor](/docs/teams/#editor-role), you also have access to [premium features](studio-pro-plan)! However, if you want to create your own teams, you need to [upgrade to your own Pro or Team subscription](upgrade).
</Callout>

### How to sign up

You can sign up for a Stately Studio account from the [**Sign in page**](https://stately.ai/registry/login) or the **Sign in** button in [the editor](https://stately.ai/editor)’s top bar.

Read more about [signing up for Stately Studio](sign-up).

### How to upgrade

You can [upgrade](upgrade) when you’re signed into Stately Studio using the **Upgrade** button in the editor’s footer.

## Priority support

When you sign up for a paid plan, you will receive an email with information on accessing priority support from the Stately team.

## Canceling your Team plan

You can cancel your Team plan at any time. When you cancel your Team plan:

* Your projects become read-only.
* New team projects can no longer be created.
* Team members can no longer be added to your team.
* The account owner can delete the teams.
* The account owner can move projects from their teams to My Projects where they will be come public unless the account owner has a Pro subscription.
* Team members’ private projects will become read-only.
* Team members will lose access to Pro features unless they have purchased their own Pro subscription.

<Callout title="Why pay for a premium plan?">
  Our premium plans include private and unlisted projects, AI assistance with Stately AI, live workflows with Stately Sky, useful features for team management and much more. Your subscription also helps support the Stately team as we build and improve Stately Studio and XState.
</Callout>


# Stately Studio (/docs/studio)









import { HelpCircle, GraduationCap, MoreHorizontal, ThemedImage } from 'lucide-react';

Stately Studio is a suite of tools for building app logic, including the Editor, [developer tools for XState](developer-tools), and much more coming soon.

<a href="https://state.new?utm_source=stately-docs&utm_medium=referral&utm_campaign=studio-docs">
  <h2>
    🚀 Open Stately Studio (state.new)
  </h2>
</a>

{/* 
  <p>
  <ThemedImage
    alt="A dog walk machine open in Stately Studio's editor. The dog walk machine has cute puppy images for each state, showing a dog walking and running. The speed up event is selected, and information and options for that transition is shown in an inspector panel on the right."
    sources={{
      light: '/studio.png',
      dark: '/studio-dm.png',
    }}
  />
  </p> */}

<img alt="A dog walk machine open in Stately Studio's editor. The dog walk machine has cute puppy images for each state, showing a dog walking and running. The speed up event is selected, and information and options for that transition is shown in an inspector panel on the right." src={__img0} placeholder="blur" />

You can use [Stately Studio’s editor](/studio.mdx#stately-studios-editor) to model your logic using state machines and statecharts visually; no code required! Collaborate on your machines with coworkers and friends with [shared projects in teams](#projects-and-teams). Use the [XState VS Code extension](xstate-vscode-extension) to use the editor with your codebase inside your code editor, or [export](#export) your code from Stately Studio into your codebase.

<Callout>
  What are state machines and statecharts? We’re glad you asked! [Check out our no-code introduction to state machines and statecharts](state-machines-and-statecharts).
</Callout>

## Stately Studio’s editor

Stately Studio’s editor supports everything you need to visually build state machines and statecharts. The editor currently has two modes; [**Design** mode](design-mode) for creating your machines and [**Simulate** mode](simulate-mode) for simulating how your machine works.

<Callout>
  [Learn how to use Stately in our quick introduction video (7m36s)](https://www.youtube.com/watch?v=EzYIerEutgk).
</Callout>

### Quick start tutorials

You can access quick start tutorials from the blue <HelpCircle size={12} /> Help button in the editor.

When you first visit the Editor, you’ll be shown two short videos (7m36s) as a quick start guide. You can access these videos again anytime from:

* Editor menu > Help > <GraduationCap size={12} /> **Learn Stately**.
* The <HelpCircle size={12} /> Help button > <GraduationCap size={12} /> **Learn Stately**.

### Projects and teams

{/* <p>
  <ThemedImage
    alt="Stately Studio Projects page for the Voyager team, showing a list of four projects, one with public machines, two with private machines, and one with unlisted machines."
    sources={{
      light: '/projects.png',
      dark: '/projects-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Projects page for the Voyager team, showing a list of four projects, one with public machines, two with private machines, and one with unlisted machines." src={__img1} placeholder="blur" />

As a [premium user](https://stately.ai/pricing), you can create and join teams in Stately Studio to share your machines and collaborate on private team projects. [Read more about Projects](projects) and [Teams](teams).

{/* <p>
  <ThemedImage
    alt="Stately Studio Team page for the Voyager team, showing Captain Janeway with the owner and Admin role, Commander Chakotay with an Admin role, Lieutenant Tuvok, Lieutenant Torres, and Lieutenant Paris with Editor roles, and Ensign Kim with a Viewer role."
    sources={{
      light: '/teams.png',
      dark: '/teams-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Team page for the Voyager team, showing Captain Janeway with the owner and Admin role, Commander Chakotay with an Admin role, Lieutenant Tuvok, Lieutenant Torres, and Lieutenant Paris with Editor roles, and Ensign Kim with a Viewer role." src={__img2} placeholder="blur" />

### Discover machines

Are you seeking inspiration for your machine? Or do you want to learn from how somebody else models their machines? The [Discover page](https://stately.ai/registry/discover) lists all the public machines created with Stately Studio. [Read more about the Discover page](discover).

{/* <p>
  <ThemedImage
    alt="Stately Studio Discover page showing the search results for “auth”, filtered by editor machines under 10 states, showing 173 results."
    sources={{
      light: '/discover.png',
      dark: '/discover-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Discover page showing the search results for &#x22;auth&#x22;, filtered by editor machines under 10 states, showing 173 results." src={__img3} placeholder="blur" />

## Our roadmap

Do you want to request a feature in Stately Studio? [Check out our roadmap](https://feedback.stately.ai) to post your feature ideas and upvote other features. Our roadmap also shows you the features we have planned and those already in progress.

You can also keep up with the Stately team’s work in progress at our regular [office hour live streams](https://youtube.com/statelyai).


# Systems (/docs/system)

An actor system is a collection of actors that can communicate with each other. Actors can invoke/spawn other actors, which forms a natural hierarchy of actors that belong to the same system.

In XState, a system is implicitly created from the root actor, which is the actor that is returned from `createActor(machine).start()`. The system can be accessed from the `actor.system` property of actors, and from the destructured `{ system }` property from state machine actions:

```ts
import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  // [!code highlight:1]
  entry: ({ system }) => {
    // ...
  },
});

const actor = createActor(machine).start();
// [!code highlight:1]
actor.system;
```

The root of a system can also be explicitly assigned a `systemId` in the `createActor(...)` function:

```ts
import { createActor } from 'xstate';

const actor = createActor(machine, {
  systemId: 'root-id',
});

actor.start();
```

This is useful for actors in the system to be able send events to the root actor.

<Callout>
  Coming soon… systems in Stately Studio’s editor 🤫
</Callout>

## Actor registration

Actors can be registered with the system so that any other actor in the system can obtain a reference to it.

Invoked actors are registered with a system-wide `systemId` in the `invoke` object:

```ts
import { createMachine, createActor, sendTo } from 'xstate';

const formMachine = createMachine({
  // ...
  on: {
    submit: {
      // [!code highlight:1]
      actions: sendTo(({ system }) => system.get('notifier'), {
        type: 'notify',
        message: 'Form submitted!',
      }),
    },
  },
});

const feedbackMachine = createMachine({
  invoke: {
    // [!code highlight:1]
    systemId: 'formMachine',
    src: formMachine,
  },
  // ...
  states: {
    // ...
    form: {
      invoke: formMachine,
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();
```

Spawned actors are registered with a system-wide `systemId` in the 2nd argument of the `spawn` function:

```ts
import { createMachine, createActor, assign } from 'xstate';

const todoMachine = createMachine({
  // ...
});

const todosMachine = createMachine({
  // ...
  on: {
    'todo.add': {
      actions: assign({
        todos: ({ context, spawn }) => {
          const newTodo = spawn(todoMachine, {
            // [!code highlight:1]
            systemId: `todo-${context.todos.length}`,
          });

          return context.todos.concat(newTodo);
        },
      }),
    },
  },
});
```

## Actor communication

You can also reference a specific actor from the system using `system.get('actorId')`:

## Stopping a system

* Stop from root actor: `actor.stop()`
* Cannot stop from descendant actors
  * Warning will be logged

## Systems and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

* `invoke.systemId`
* `spawn(thing, { systemId })`
* `system.get('actorId')`
* `rootActor.stop()`

## System cheatsheet

### Cheatsheet: actor system

```ts
import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  // [!code highlight:1]
  entry: ({ system }) => {
    // ...
  },
});

const actor = createActor(machine).start();
// [!code highlight:1]
actor.system;
```

### Cheatsheet: explicitly assign a `systemId`

```ts
import { createActor } from 'xstate';

const actor = createActor(machine, {
  // [!code highlight:1]
  systemId: 'root-id',
});

actor.start();
```

### Cheatsheet: register an invoked actor with the system

```ts
import { createMachine, createActor, sendTo } from 'xstate';

const formMachine = createMachine({
  // ...
  on: {
    submit: {
      // [!code highlight:1]
      actions: sendTo(({ system }) => system.get('notifier'), {
        type: 'notify',
        message: 'Form submitted!',
      }),
    },
  },
});

const feedbackMachine = createMachine({
  invoke: {
    // [!code highlight:1]
    systemId: 'formMachine',
    src: formMachine,
  },
  // ...
  states: {
    // ...
    form: {
      invoke: formMachine,
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();
```

### Cheatsheet: register a spawned actor with the system

```ts
import { createMachine, createActor, assign } from 'xstate';

const todoMachine = createMachine({
  // ...
});

const todosMachine = createMachine({
  // ...
  on: {
    'todo.add': {
      actions: assign({
        todos: ({ context, spawn }) => {
          const newTodo = spawn(todoMachine, {
            // [!code highlight:1]
            systemId: `todo-${context.todos.length}`,
          });

          return context.todos.concat(newTodo);
        },
      }),
    },
  },
});
```


# Tags (/docs/tags)

State nodes can have **tags**, which are string terms that help group or categorize the state node. For example, you can signify which state nodes represent states in which data is being loaded by using a "loading" tag, and determine if a state contains those tagged state nodes with `state.hasTag(tag)`:

```ts
const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'prompt',
  states: {
    prompt: {
      tags: ['visible'],
      // ...
    },
    form: {
      tags: ['visible'],
      // ...
    },
    thanks: {
      tags: ['visible', 'confetti'],
      // ...
    },
    closed: {
      tags: ['hidden'],
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start();

console.log(feedbackActor.getSnapshot().hasTag('visible'));
// logs true
```

## Tags and TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

You can strongly type the `tags` of your machine in the `types.tags` property of the machine setup.

```ts
import { setup } from 'xstate';

const machine = setup({
  types: {
    // [!code highlight:1]
    tags: {} as 'pending' | 'success' | 'error',
  },
}).createMachine({
  // ...
  states: {
    loadingUser: {
      tags: ['pending'], // Strongly-typed
    },
  },
});

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

actor
  .getSnapshot()
  // Autocompleted
  .hasTag('pending');
```


# Teams (/docs/teams)





import { MoreHorizontal } from 'lucide-react';

You can create and join teams in Stately Studio to share and collaborate on team projects.

{/* <p>
  <ThemedImage
    alt="Stately Studio Team page for the Intro to Stately Studio webinar team, showing Laura Kalbag with the owner and Admin role, Anders Bech Mellson and Kevin Maes with Admin roles, and Rejected Proud System and David Khourshid with Viewer roles."
    sources={{
      light: '/teams.png',
      dark: '/teams-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Team page for the Intro to Stately Studio webinar team, showing Laura Kalbag with the owner and Admin role, Anders Bech Mellson and Kevin Maes with Admin roles, and Rejected Proud System and David Khourshid with Viewer roles." src={__img0} placeholder="blur" />

<Callout>
  Teams and shared projects are [Team features](studio-team-plan) of Stately Studio. [Check out the features on our Team plan](https://stately.ai/pricing) or [upgrade to the Team plan](https://stately.ai/registry/billing).
</Callout>

## Team roles

Team members can have one of the following roles: [Owner](teams.mdx#owner-role), [Admin](teams.mdx#admin-role), [Editor](teams.mdx#editor-role), or [Viewer](teams.mdx#viewer-role). Team owners or admins assign the role to the new team member when inviting them to join the team.

The following table is an overview of the capabilities of each team role:

| Capability                               | Owner | Admin | Editor | Viewer |
| ---------------------------------------- | ----- | ----- | ------ | ------ |
| View team projects and machines          | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| View other team members                  | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| Export team machines                     | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| Fork team projects and machines          | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |
| Edit existing team projects              | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |
| Create team projects                     | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |
| Lock team machines                       | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |
| Reassign Admin, Editor, and Viewer roles | ✅ Yes | ✅ Yes | 🚫 No  | 🚫 No  |
| Invite new team members                  | ✅ Yes | ✅ Yes | 🚫 No  | 🚫 No  |
| Change project visibility                | ✅ Yes | ✅ Yes | 🚫 No  | 🚫 No  |
| Edit the team name                       | ✅ Yes | ✅ Yes | 🚫 No  | 🚫 No  |
| Delete the team                          | ✅ Yes | 🚫 No | 🚫 No  | 🚫 No  |

### Owner role

Team subscribers can create teams and they become their teams’ owner by default. The team owner can:

* Manage their account billing, including changes to the number of paid seats for their teams.
* Create new teams and delete teams
* Edit the team name
* Change project visibility
* Invite new team members
* View team members
* Give team members Admin, Editor, and Viewer roles
* Create team projects
* Lock team machines
* Edit, fork, export, and view team projects and machines

#### Teams members and the available plan seats

Creating a team requires the Team plan of Stately Studio, with includes up to 10 seats. That means one seat for the Team subscription owner plus 9 additional seats (Viewers are free). These seats can be allocated to one or more teams, in any configuration desired.

### Admin role

Users with the Admin role can:

* Edit the team name
* Change project visibility
* Invite new team members
* View team members
* Give team members Editor and Viewer roles
* Create team projects
* Lock team machines
* Edit, fork, export, and view team projects and machines

### Editor role

Users with the Editor role can:

* View team members
* Create team projects
* Lock team machines
* Edit, fork, export, and view team projects and machines

### Viewer role

<Callout>
  A Viewer is free of charge and does not count toward your number of seats.
</Callout>

Users with the Viewer role can:

* View team members
* View team projects and machines
* Export team machines

## Create a team

1. Use the **Create team** link in the left sidebar of Stately Studio’s workspace to open the Create team form.
2. Name your team and use the **Create team** button to create your team.

By default, teams have only one member, the creator and Owner. [Invite new members](teams.mdx#invite-new-members-to-a-team) to add more members to your team.

## Edit the team name

1. Select the team from the left sidebar of Stately Studio’s workspace.
2. Navigate to the Settings tab.
3. Edit the team name and use the **Save changes** button to update your team name.

## Invite new members to a team

You need to have [enough seats on your Pro plan](#teams-members-and-the-available-plan-seats) to add more members to your team.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. On your team page, use the **Members** tab to view existing members and their roles and invite new members.
3. Enter the invitee’s email address and choose their role from the dropdown menu.
4. Use the **Invite to team** button to send the invitation.

The invitation will be sent to the invitee’s email address. The invitee will appear as a team member in the **Pending** section of the **Members** page until they accept the invitation. Once the invitation is accepted, the team member will be listed on the **Members** page, organized by role.

<Callout>
  A subscription seat is only taken up at the time of accepting the invitation. Any particular team member is considered to occupy a single seat even if they are a member of more than one team.
</Callout>

## Change a team member’s role

To change a team member’s role, you must have the [Owner role](teams.mdx#owner-role) or [Admin role](teams.mdx#admin-role) for that team.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. Navigate to the **Members** tab on the team page.
3. Use the <MoreHorizontal size={18} /> triple dot contextual menu alongside the team member’s name in the **Members** list to choose from the options **Make a Viewer**, **Make an Editor**, or **Make an Admin**.

## Remove a member from a team

To remove a member from a team, you must have the [owner role](teams.mdx#owner-role) or [Admin role](teams.mdx#admin-role) for that team.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. Navigate to the **Members** tab on the team page.
3. Use the <MoreHorizontal size={18} /> triple dot contextual menu alongside the team member’s name in the **Members** list to choose the **Remove from team** option.

## Leave a team

Any team member with **Admin**, **Editor**, or **Viewer** roles can leave a team.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. Navigate to the **Members** tab on the team page.
3. Use the <MoreHorizontal size={18} /> triple dot contextual menu alongside the team member’s name in the **Members** list to choose the **Leave team** option.

Team owners cannot leave their own teams, but they can delete the team.

## Delete a team

Only team owners can delete a team.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. Navigate to the **Settings** tab on the team page.
3. Use the **Delete team** button to delete your team.
4. Confirm the team deletion using the **Delete** button in the Delete team dialog.

## Create shared team projects

Shared projects can be created and worked on between team members of any given team. Team projects are listed inside their Team in the Stately Studio workspace. Projects are Private by default, but can be made Public or Unlisted.

{/* <p>
  <ThemedImage
    alt="Stately Studio Projects page for the Voyager team, showing a list of four projects, one with public machines, two with private machines, and one with unlisted machines."
    sources={{
      light: '/projects.png',
      dark: '/projects-dm.png',
    }}
  />
  </p> */}

<img alt="Stately Studio Projects page for the Voyager team, showing a list of four projects, one with public machines, two with private machines, and one with unlisted machines." src={__img1} placeholder="blur" />

#### Create a shared team project

Only team members with the **Owner**, **Admin**, or **Editor** roles can create team projects.

1. Navigate to the team page from the left sidebar of Stately Studio’s workspace.
2. Navigate to the **Projects** tab on the team page.
3. Use the **Create project** button.
4. Name the project. The description and keyword fields are optional.
5. Specify the Project Visibility as **Public**, **Private**, or **Unlisted** in the dropdown.
6. Use the **Submit** button to create the new project.

### Move a project between teams

You can move projects between your teams if you have an [**Owner**](teams.mdx#owner-role) role on both teams.

1. Use the **Share** button in Stately Studio’s top bar to open the Share dialog.
2. Choose the destination team from the dropdown menu in the Move project section.
3. Confirm that you want to move the project from the project move warning dialog using the **Move to *project name*** button.


# Templates (/docs/templates)

XState runs anywhere you can run JavaScript and is framework-agnostic.

You can get started with or without your framework of choice using the templates below.

<table>
  <thead>
    <tr>
      <th>Template</th>

      <th>Uses</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        **🤖 XState Template** <br />
        [Open in CodeSandbox](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vanilla-ts) <br />
        [Open in StackBlitz](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/vanilla-ts?file=%2Fsrc%2FfeedbackMachine.ts)
      </td>

      <td>
        * XState v5
        * TypeScript
        * *No framework*
      </td>
    </tr>

    <tr>
      <td>
        **⚛️ XState + React Template** <br />
        [Open in CodeSandbox](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/react-ts) <br />
        [Open in StackBlitz](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/react-ts?file=%2Fsrc%2FfeedbackMachine.ts)
      </td>

      <td>
        * [React](https://react.dev/)
        * XState v5
        * TypeScript
      </td>
    </tr>

    <tr>
      <td>
        **💚 XState + Vue Template** <br />
        [Open in CodeSandbox](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vue-ts) <br />
        [Open in StackBlitz](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/vue-ts?file=%2Fsrc%2FfeedbackMachine.ts)
      </td>

      <td>
        * [Vue](https://vuejs.org/)
        * XState v5
        * TypeScript
      </td>
    </tr>
  </tbody>
</table>


# Testing (/docs/testing)

## Testing logic

Testing actor logic is important for ensuring that the logic is correct and that it behaves as expected. You can test your state machines and actors using various testing libraries and tools. You should follow the **Arrange, Act, Assert** pattern when writing tests for your state machines and actors:

* **Arrange** - set up the test by creating the actor logics (such as a state machine) and the actors from the actor logics.
* **Act** - send event(s) to the actor(s).
* **Assert** - assert that the actor(s) reached their expected state(s) and/or executed the expected side effects.

```ts
import { setup, createActor } from 'xstate';
import { test, expect } from 'vitest';

test('some actor', async () => {
  const notifiedMessages: string[] = [];

  // 1. Arrange
  const machine = setup({
    actions: {
      notify: (_, params) => {
        notifiedMessages.push(params.message);
      },
    },
  }).createMachine({
    initial: 'inactive',
    states: {
      inactive: {
        on: { toggle: { target: 'active' } },
      },
      active: {
        entry: { type: 'notify', params: { message: 'Active!' } },
        on: { toggle: { target: 'inactive' } },
      },
    },
  });

  const actor = createActor(machine);

  // 2. Act
  actor.start();
  actor.send({ type: 'toggle' }); // => should be in 'active' state
  actor.send({ type: 'toggle' }); // => should be in 'inactive' state
  actor.send({ type: 'toggle' }); // => should be in 'active' state

  // 3. Assert
  expect(actor.getSnapshot().value).toBe('active');
  expect(notifiedMessages).toEqual(['Active!', 'Active!']);
});
```

<Callout>
  You can now [generate test paths from your state machines in Stately Studio](generate-test-paths). You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

## Testing actors

When testing actors, you typically want to verify that they transition to the correct state and update their context appropriately when receiving events.

```ts
import { setup, createActor } from 'xstate';
import { test, expect } from 'vitest';

test('actor transitions correctly', () => {
  const toggleMachine = setup({}).createMachine({
    initial: 'inactive',
    context: { count: 0 },
    states: {
      inactive: {
        on: { 
          activate: { 
            target: 'active',
            actions: assign({ count: ({ context }) => context.count + 1 })
          }
        }
      },
      active: {
        on: { 
          deactivate: 'inactive' 
        }
      }
    }
  });

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

  // Test initial state
  expect(actor.getSnapshot().value).toBe('inactive');
  expect(actor.getSnapshot().context.count).toBe(0);

  // Send event and test transition
  actor.send({ type: 'activate' });
  
  expect(actor.getSnapshot().value).toBe('active');
  expect(actor.getSnapshot().context.count).toBe(1);

  // Send another event
  actor.send({ type: 'deactivate' });
  
  expect(actor.getSnapshot().value).toBe('inactive');
  expect(actor.getSnapshot().context.count).toBe(1);
});
```

## Mocking effects

When testing state machines that have side effects (like API calls, logging, or other external interactions), you should mock these effects to make your tests deterministic and isolated.

```ts
import { setup, createActor } from 'xstate';
import { test, expect, vi } from 'vitest';

test('mocking actions', () => {
  const mockLogger = vi.fn();
  
  const machine = setup({
    actions: {
      // Mock the logging action
      logMessage: mockLogger
    }
  }).createMachine({
    initial: 'idle',
    states: {
      idle: {
        on: {
          start: {
            target: 'running',
            actions: { 
              type: 'logMessage', 
              params: { message: 'Started!' } 
            }
          }
        }
      },
      running: {}
    }
  });

  const actor = createActor(machine);
  actor.start();
  
  actor.send({ type: 'start' });
  
  expect(actor.getSnapshot().value).toBe('running');
  expect(mockLogger).toHaveBeenCalledWith(
    expect.anything(), // action meta
    { message: 'Started!' } // params
  );
});
```

For promise-based actors, you can mock the promises:

```ts
test('mocking promise actors', async () => {
  const mockFetch = vi.fn().mockResolvedValue({ data: 'test' });
  
  const machine = setup({
    actors: {
      fetchData: fromPromise(mockFetch)
    }
  }).createMachine({
    initial: 'idle',
    states: {
      idle: {
        on: {
          fetch: 'loading'
        }
      },
      loading: {
        invoke: {
          src: 'fetchData',
          onDone: 'success',
          onError: 'error'
        }
      },
      success: {},
      error: {}
    }
  });

  const actor = createActor(machine);
  actor.start();
  
  actor.send({ type: 'fetch' });
  
  // Wait for promise to resolve
  await new Promise(resolve => setTimeout(resolve, 0));
  
  expect(actor.getSnapshot().value).toBe('success');
  expect(mockFetch).toHaveBeenCalled();
});
```

## Using `@xstate/test`

<Callout type="warning">
  The XState Test model-based testing utilities have moved into `xstate` itself and are now available under `xstate/graph`. The standalone `@xstate/test` package is deprecated in favor of the integrated testing utilities.
</Callout>

The model-based testing utilities allow you to automatically generate test cases from your state machines, ensuring comprehensive coverage of all possible paths and edge cases.


# Transition Actors (/docs/transition-actors)

Transition actors are actors whose logic is represented by a **state-transition function**, which is a function that returns the **next state** of an actor given:

* The **current state** of the actor
* The **event** that triggered the transition

This is very similar to a *reducer function* in libraries like Redux.

## Transition actor capabilities

|   | Capability     | Notes                                                                                                                                                 |
| - | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅ | Receive events | Transition actors receive events to modify its state.                                                                                                 |
| ✅ | Send events    | Transition actors can send events to other actors it has reference to, such as those provided in its `input`. Note that doing this would be *impure*. |
| ❌ | Spawn actors   | Transition actors currently cannot spawn new actors.                                                                                                  |
| ✅ | Input          | You can provide `input` to transition actors.                                                                                                         |
| ❌ | Output         | Transition actors currently do not produce output – they are active indefinitely until they are stopped or an error occurs.                           |

## Transition actor logic

You can define transition actor logic using the `fromTransition(...)` actor logic creator, which takes two arguments:

* A **state-transition function** that returns the next state of the actor
* An **initial state** for the actor

The actor logic creator returns actor logic that can be used to create transition actors.

```ts
import { fromTransition, createActor } from 'xstate';

const countLogic = fromTransition(
  (state, event) => {
    switch (event.type) {
      case 'increment': {
        return { count: state.count + 1 };
      }
      case 'decrement': {
        return { count: state.count - 1 };
      }
      default: {
        return state;
      }
    }
  },
  { count: 0 },
); // Initial state

const countActor = createActor(countLogic);
countActor.subscribe((snapshot) => {
  console.log(snapshot.context);
});
countActor.start();
// logs { count: 0 }

countActor.send({ type: 'increment' });
// logs { count: 1 }

countActor.send({ type: 'decrement' });
// logs { count: 0 }
```

## Transition actor input

You can pass in `input` to a transition actor, which will be passed to the function that resolves the initial state.

```ts
import { fromTransition, createActor } from 'xstate';

const countLogic = fromTransition(
  (state, event) => {
    // ...
    // [!code highlight:5]
  },
  ({ input }: { input: number }) => ({
    count: input, // Initial state
  }),
);

const countActor = createActor(countLogic, {
  // [!code highlight:1]
  input: 42,
});

countActor.subscribe((snapshot) => {
  console.log(snapshot.context);
  // logs { count: 42 }
});

countActor.start();
```


# Events and transitions (/docs/transitions)

A **transition** is a change from one finite state to another, triggered by an event.

An **event** is a signal, trigger, or message that causes a transition. When an actor receives an event, its machine will determine if there are any enabled transitions for that event in the current state. If enabled transitions exist, the machine will take them and execute their actions.

<Callout>
  You can visualize your state machines and easily add transitions in our drag-and-drop Stately editor. [Read more about transitions in Stately's editor](/docs/editor-states-and-transitions/#transitions-and-events).
</Callout>

<EmbedMachine name="Video player" embedURL="https://stately.ai/registry/editor/embed/e13bef2b-bb13-4465-96ac-0bc25340688e?machineId=9630e3b7-9f8e-4dc9-8b55-661f854d28b7&mode=Simulate" />

Transitions are "deterministic"; each combination of state and event always points to the same next state. When a state machine receives an event, only the active finite states are checked to see if any of them have a transition for that event. Those transitions are called **enabled transitions**. If there is an enabled transition, the state machine will execute the transition's actions, and then transition to the target state.

Transitions are represented by `on:` in a state:

```ts
import { createMachine } from 'xstate';
const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        'feedback.good': {
          target: 'thanks',
        },
      },
    },
    thanks: {},
  },
});
```

## Event objects

In XState, events are represented by event objects with a `type` property and optional payload:

* The `type` property is a string that represents the event type.
* The payload is an object that contains additional data about the event.

```ts
feedbackActor.send({
  // The event type
  type: 'feedback.update',
  // Additional payload
  feedback: 'This is great!',
  rating: 5,
});
```

## Selecting transitions

Transitions are selected by checking the deepest child states first. If the transition is enabled (i.e. if its guard passes), it will be taken. If not, the parent state will be checked, and so on.

1. Start on the deepest active state nodes (aka atomic state nodes)
2. If the transition is enabled (no `guard` or its `guard` evaluates to `true`), select it.
3. If no transition is enabled, go up to the parent state node and repeat step 1.
4. Finally, if no transitions are enabled, no transitions will be taken, and the state will not change.

## Self-transitions

A state can transition to itself. This is known as a **self-transition**, and is useful for changing context and/or executing actions without changing the finite state. You can also use self-transitions to restart a state.

**Root self-transitions:**

```ts
import { createMachine, assign } from 'xstate';

const machine = createMachine({
  context: { count: 0 },
  // [!code highlight:8]
  on: {
    someEvent: {
      // No target
      actions: assign({
        count: ({ context }) => context.count + 1,
      }),
    },
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=91da1d57-b146-48fd-82ce-a9dd28b7261a" title="Root self-transition" />

**Self-transitions on states:**

```ts
import { createMachine, assign } from 'xstate';

const machine = createMachine({
  context: { count: 0 },
  initial: 'inactive',
  states: {
    inactive: {
      on: { activate: { target: 'active' } },
    },
    active: {
      // [!code highlight:8]
      on: {
        someEvent: {
          // No target
          actions: assign({
            count: ({ context }) => context.count + 1,
          }),
        },
      },
    },
  },
});
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=8763e570-3535-42b3-a2a2-8edd82d1207a" title="Self-transition on state" />

<Callout>
  You can easily visualize self-transitions in Stately's editor. [Read more about self-transitions in Stately's editor](/docs/editor-states-and-transitions/#self-transitions).
</Callout>

## Transitions between states

Usually, transitions are between two sibling states. These transitions are defined by setting the `target` as the sibling state key.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  states: {
    form: {
      on: {
        submit: {
          // [!code highlight:2]
          // Target is the key of the sibling state
          target: 'submitting',
        },
      },
    },
    // [!code highlight:1]
    submitting: {
      // ...
    },
  },
});
```

## Parent to child transitions

When a state machine actor receives an event, it will first check the deepest ([atomic](state-machines-and-statecharts.mdx#atomic-states)) state to see if there is any enabled transition. If not, the parent state is checked, and so on, until the state machine reaches the root state.

When you want an event to transition to a state regardless of which sibling state is active, a useful pattern is to transition from the parent state to the child state.

For example, the below state machine will transition to the `colorMode.system` state on the `mode.reset` event regardless of which state it is currently in.

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  id: 'colorMode',
  initial: 'system',
  states: {
    system: {},
    auto: {},
    light: {
      on: {
        'mode.toggle': { target: 'dark' },
      },
    },
    dark: {
      on: {
        'mode.toggle': { target: 'light' },
      },
    },
  },
  on: {
    'mode.reset': {
      // [!code highlight:1]
      target: '.system',
    },
  },
});
```

## Re-entering

By default, when a state machine transitions from some state to the same state or from a parent state to a descendent (child, grandchild, etc.) of that parent state, it will not re-enter the state; that is, it will not execute the [`exit` and `entry` actions](actions) of the parent state. It will not stop existing invoked actors or start new invoked actors.

This can be changed with the transition `reenter` property: if you want the parent state to be re-entered, you can set `reenter: true`. This will cause the state to re-enter when transitioning to itself or descendent states, executing the `exit` and `entry` actions of the state. It will stop existing invoked actors, and start new invoked actors.

<Callout>
  In XState v4, re-entering transitions were known as **external transitions**, and the default transitions were known as **internal transitions**.
</Callout>

**Self-transitions with `reenter: true`:**

```ts
import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  initial: 'someState',
  states: {
    someState: {
      entry: () => console.log('someState entered'),
      exit: () => console.log('someState exited'),
      on: {
        'event.normal': {
          target: 'someState', // or no target
        },
        'event.thatReenters': {
          target: 'someState', // or no target
          reenter: true,
        },
      },
    },
  },
});

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

actor.send({ type: 'event.normal' });
// Does not log anything

actor.send({ type: 'event.thatReenters' });
// Logs:
// "someState exited"
// "someState entered"
```

**Parent-child (or descendent) transitions with `reenter: true`:**

```ts
import { createMachine, createActor } from 'xstate';

const machine = createMachine({
  initial: 'parentState',
  states: {
    parentState: {
      entry: () => console.log('parentState entered'),
      exit: () => console.log('parentState exited'),
      on: {
        'event.normal': {
          target: '.someChildState',
        },
        'event.thatReenters': {
          target: '.otherChildState',
          reenter: true,
        },
      },
      initial: 'someChildState',
      states: {
        someChildState: {
          entry: () => console.log('someChildState entered'),
          exit: () => console.log('someChildState exited'),
        },
        otherChildState: {
          entry: () => console.log('otherChildState entered'),
          exit: () => console.log('otherChildState exited'),
        },
      },
    },
  },
});

const actor1 = createActor(machine);
actor1.start();
actor1.send({ type: 'event.normal' });
// Logs:
// "someChildState exited"
// "someChildState entered"

const actor2 = createActor(machine);
actor2.start();
console.log('---');
actor2.send({ type: 'event.thatReenters' });
// Logs:
// "someChildState exited"
// "parentState exited"
// "parentState entered"
// "otherChildState entered"
```

## Transitions to any state

Sibling descendent states: `{ target: 'sibling.child.grandchild' }`

Parent to descendent states: `{ target: '.child.grandchild' }`

State to any state: `{ target: '#specificState' }`

## Forbidden transitions

* `{ on: { forbidden: {} } }`
* Different than omitting the transition; transition selection algorithm will stop looking
* Same as `{ on: { forbidden: { target: undefined } } }`

## Wildcard transitions

A wildcard transition is a transition that will match any event. The event descriptor (key of the `on: {...}` object) is defined using the `*` wildcard character as the event type:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'asleep',
  states: {
    asleep: {
      on: {
        // [!code highlight:2]
        // This transition will match any event
        '*': { target: 'awake' },
      },
    },
    awake: {},
  },
});
```

Wildcard transitions are useful for:

* handling events that are not handled by any other transition.
* as a "catch-all" transition that handles any event in a state.

A wildcard transition has the least priority; it will only be taken if no other transitions are enabled.

## Partial wildcard transitions

A partial wildcard transition is a transition that matches any event that starts with a specific prefix. The event descriptor is defined by using the wildcard character (`*`) after a dot (`.`) as the event type:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        // [!code highlight:4]
        // This will match the 'feedback' event as well as
        // any event that starts with 'feedback.', e.g.:
        // 'feedback.good', 'feedback.bad', etc.
        'feedback.*': { target: 'form' },
      },
    },
    form: {},
    // ...
  },
});
```

The wildcard character (`*`) can only be used in the suffix of an event descriptor, following a dot (`.`):

### Valid wildcard examples

* ✅ `mouse.*`: matches `mouse`, `mouse.click`, `mouse.move`, etc.
* ✅ `mouse.click.*`: matches `mouse.click`, `mouse.click.left`, `mouse.click.right`, etc.

### Invalid wildcard

* 🚫 ~~`mouse*`~~: invalid; does not match any event.
* 🚫 ~~`mouse.*.click`~~: invalid; `*` cannot be used in the middle of an event descriptor.
* 🚫 ~~`*.click`~~: invalid; `*` cannot be used in the prefix of an event descriptor.
* 🚫 ~~`mouse.click*`~~: invalid; does not match any event.
* 🚫 ~~`mouse.*.*`~~: invalid; `*` cannot be used in the middle of an event descriptor.

## Multiple transitions in parallel states

Since parallel states have multiple regions that can be active at the same time, it is possible for multiple transitions to be enabled at the same time. In this case, all enabled transitions to these regions will be taken.

Multiple targets are specified as an array of strings:

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  type: 'parallel',
  states: {
    mode: {
      initial: 'light',
      states: {
        light: {
          on: {
            toggle: { target: 'dark' },
          },
        },
        dark: {
          on: {
            toggle: { target: 'light' },
          },
        },
      },
    },
    theme: {
      initial: 'default',
      states: {
        default: {
          on: {
            change: { target: 'custom' },
          },
        },
        custom: {
          on: {
            change: { target: 'default' },
          },
        },
      },
    },
  },
  on: {
    // This event will transition both regions to specific states
    'set.dark.custom': {
      // [!code highlight:1]
      target: ['.mode.dark', '.theme.custom'],
    },
    // This event will transition one region while leaving the other unchanged
    'set.light': {
      target: '.mode.light',
    },
  },
});
```

In this example:

* The `set.dark.custom` event will transition both regions simultaneously: the `mode` region to `dark` and the `theme` region to `custom`
* The `set.light` event will only transition the `mode` region to `light` while leaving the `theme` region in its current state
* Each region can still be controlled independently through their own events (`toggle` and `change`)

## Other transitions

* [**Eventless (always) transitions**](eventless-transitions) are transitions without events. These transitions are *always* taken after any transition in their state is enabled.
* [**Delayed (after) transitions**](delayed-transitions) are transitions that are enabled after a specified duration.

## Transition descriptions

You can add a `.description` string to a transition to describe the transition. This is useful for explaining the purpose of the transition in the visualized state machine.

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  // ...
  on: {
    exit: {
      // [!code highlight:1]
      description: 'Closes the feedback form',
      target: '.closed',
    },
  },
});
```

## Shorthands

If the transition only specifies a `target`, then the string target can be used as a shorthand instead of the entire transition object:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  initial: 'prompt',
  states: {
    prompt: {
      on: {
        // [!code highlight:3]
        // This is shorthand for:
        // 'feedback': { target: 'form' }
        'feedback.good': 'thanks',
      },
    },
    thanks: {},
    // ...
  },
});
```

Using the string target shorthand is useful for quickly prototyping state machines. Generally, we recommended using the full transition object syntax as it will be consistent with all other transition objects and will be easier to add actions, guards, and other properties to the transition in the future.

## TypeScript

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the latest TypeScript version. [Read more about XState and TypeScript](typescript)
</Callout>

Transitions mainly use the event type that they are enabled by.

```ts
import { setup } from 'xstate';

const machine = setup({
  types: {
    // [!code highlight:1]
    events: {} as { type: 'greet'; message: string } | { type: 'submit' },
  },
}).createMachine({
  // ...
  on: {
    greet: {
      actions: ({ event }) => {
        event.type; // 'greet'
        event.message; // string
      },
    },
  },
});
```

## Frequently asked questions

<details>
  <summary>
    How can I listen for events sent to actors?
  </summary>

  You can use the [inspection API](./inspection) to listen for all inspection events in an actor system. The `@xstate.event` inspection event contains information about events sent from one actor to another (or itself):

  ```ts
  import { createActor } from 'xstate';
  import { someMachine } from './someMachine';

  const actor = createActor(someMachine, {
    inspect: (inspectionEvent) => {
      if (inspectionEvent.type === '@xstate.event') {
        // [!code highlight:2]
        // The event object sent from one actor to another
        console.log(inspectionEvent.event);
      }
    },
  });
  ```
</details>

## Transitions cheatsheet

Use our XState events and transitions cheatsheet below to get started quickly.

### Cheatsheet: event objects

```ts
feedbackActor.send({
  // Event type
  type: 'feedback.update',
  // Event payload
  feedback: 'A+ would use state machines again',
  rating: 5,
});
```

### Cheatsheet: transition targets

```ts
import { createMachine } from 'xstate';

const machine = createMachine({
  initial: 'a',
  states: {
    a: {
      on: {
        // Sibling target
        event: {
          target: 'b',
        },
        // Sibling child target
        otherEvent: {
          target: 'b.c',
        },
      },
    },
    b: {
      on: {
        // ID target
        event: {
          target: '#c',
        },
      },
    },
    c: {
      id: 'c',
      on: {
        // Child target
        event: {
          target: '.child',
        },
      },
      initial: 'child',
      states: {
        child: {},
      },
    },
  },
  on: {
    // Child target
    someEvent: {
      target: '.b',
    },
  },
});
```

## Transitioning state

*Since XState version 5.19.0*

When you create a state machine actor, the next state is determined by the machine's current state and the event that is sent to the actor. However, you can also determine the next **state** and **actions** from the current state and event by using the pure `transition(machine, state, event)` and `initialTransition(machine)` functions:

```ts
import { createMachine, initialTransition, transition } from 'xstate';

const machine = createMachine({
  initial: 'pending',
  states: {
    pending: {
      on: {
        start: { target: 'started' },
      },
    },
    started: {
      entry: 'doSomething',
    },
  },
});

const [initialState, initialActions] = initialTransition(machine);

console.log(initialState.value);
// logs 'pending'

console.log(initialActions);
// logs []

const [nextState, actions] = transition(machine, initialState, {
  type: 'start',
});

console.log(nextState.value);
// logs 'started'

console.log(actions);
// logs [{ type: 'doSomething', … }]
```

## Determining the next state

<Callout type="warning">
  It is recommended to use the `initialTransition(…)` and `transition(…)` functions instead of `getNextSnapshot(…)` and `getInitialSnapshot(…)`, which will be deprecated.
</Callout>

When you create a state machine actor, the next state is determined by the machine's current state and the event that is sent to the actor. If you want to determine the next state outside of the actor, you can use the `getNextSnapshot(…)` function:

```ts
import { getNextSnapshot } from 'xstate';
import { feedbackMachine } from './feedbackMachine';

const nextSnapshot = getNextSnapshot(
  feedbackMachine,
  feedbackMachine.resolveState({ value: 'question' }),
  { type: 'feedback.good' },
);

console.log(nextSnapshot.value);
// logs 'thanks'
```

You can also determine the initial state of a machine using the `getInitialSnapshot(…)` function:

```ts
import { getInitialSnapshot } from 'xstate';
import { feedbackMachine } from './feedbackMachine';

const initialSnapshot = getInitialSnapshot(
  feedbackMachine,
  // optional input
  { defaultRating: 3 },
);

console.log(initialSnapshot.value);
// logs 'question'
```


# Typegen (/docs/typegen)

<Callout type="warning">
  Typegen is not supported in XState version 5.
</Callout>

Typegen is not supported in XState version 5.


# TypeScript (/docs/typescript)

XState v5 and its related libraries are written in [TypeScript](https://www.typescriptlang.org), and utilize complex types to provide the best type safety and inference possible for you.

<Callout>
  **XState v5 requires TypeScript version 5.0 or greater.**

  For best results, use the **latest TypeScript version**.
</Callout>

Follow these guidelines to ensure that your TypeScript project is ready to use XState v5:

## Use the latest version of TypeScript

Use the latest version of TypeScript; version 5.0 or greater is required.

```bash
   npm install typescript@latest --save-dev
```

## Set up your `tsconfig.json` file

* Set [`strictNullChecks`](https://www.typescriptlang.org/tsconfig#strictNullChecks) to `true` in your `tsconfig.json` file. This will ensure that our types work correctly and help catch errors in your code. **(Strongly recommended)**.
* Set [`skipLibCheck`](https://www.typescriptlang.org/tsconfig#skipLibCheck) to `true` in your `tsconfig.json` file. (Recommended).

```json5
// tsconfig.json
{
  compilerOptions: {
    // ...
    // [!code highlight:1]
    strictNullChecks: true,
    // or set `strict` to true, which includes `strictNullChecks`
    // "strict": true,

    // [!code highlight:1]
    skipLibCheck: true,
  },
}
```

## Specifying types

The recommended way to strongly type your machine is to use the `setup(...)` function:

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as { feedback: string },
    events: {} as { type: 'feedback.good' } | { type: 'feedback.bad' },
  },
  actions: {
    logTelemetry: () => {
      // TODO: implement
    },
  },
}).createMachine({
  // ...
});
```

You can also specify TypeScript types inside the [machine config](machines) using the `.types` property:

```ts
import { createMachine } from 'xstate';

const feedbackMachine = createMachine({
  types: {} as {
    context: { feedback: string };
    events: { type: 'feedback.good' } | { type: 'feedback.bad' };
    actions: { type: 'logTelemetry' };
  },
});
```

These types will be inferred throughout the machine config and in the created machine and actor so that methods such as `machine.transition(...)` and `actor.send(...)` will be type-safe.

## Dynamic parameters

It is recommended to use dynamic parameters in [actions](./actions) and [guards](./guards) as they allow you to make reusable functions that are not closely tied to the machine, and are strongly-typed.

```ts
import { setup } from 'xstate';

const feedbackMachine = setup({
  types: {
    context: {} as {
      user: { name: string };
    },
  },
  actions: {
    greet: (_, params: { name: string }) => {
      console.log(`Hello, ${params.name}!`);
    },
  },
}).createMachine({
  context: {
    user: {
      name: 'David',
    },
  },
  // ...
  entry: {
    type: 'greet',
    params: ({ context }) => ({
      name: context.user.name,
    }),
  },
});
```

## Asserting events

### Actions and Guards

<Callout>
  It is strongly recommended to use dynamic parameters instead of directly accessing the event object whenever possible for improved type safety and reusability.
</Callout>

If using dynamic parameters is infeasible and you must use the event in an action or guard implementation, you can assert the event type using the `assertEvent(...)` helper function:

```ts
import { createMachine, assertEvent } from 'xstate';

const machine = createMachine({
  types: {
    events: {} as
      | { type: 'greet'; message: string }
      | { type: 'log'; message: string }
      | { type: 'doSomethingElse' },
  },
  // ...
  states: {
    someState: {
      entry: ({ event }) => {
        // In the entry action, it is currently not possible to know
        // which event this action was called with.

        // Calling `assertEvent` will throw if
        // the event is not the expected type.
        // [!code highlight:1]
        assertEvent(event, 'greet');

        // Now we know the event is a `greet` event,
        // and we can access its `message` property.
        console.log(event.message.toUpperCase());
      },
      // ...
      exit: ({ event }) => {
        // You can also assert multiple possible event types.
        // [!code highlight:1]
        assertEvent(event, ['greet', 'log']);

        // Now we know the event is a `greet` or `log` event,
        // and we can access its `message` property.
        console.log(event.message.toUpperCase());
      },
    },
  },
});
```

### Invoked Actor Input

Another case where it helpful to use `assertEvent` is when specifying `input` for an invoked actor. The `event` received could be any one of the events received by that actor. In order for TypeScript to recognize the event type and its properties, you can use `assertEvent` to narrow down the event type.

```ts
import { createMachine, assertEvent } from 'xstate';

const machine = createMachine({
  types: {
    events: {} as
      | { type: 'messageSent'; message: string }
      | { type: 'incremented'; count: number },
  },
  actors: {
    someActor: fromPromise<void, { message: string }>(({ input }) => {
      // actor implementation
    }),
  }
  // ...
  states: {
    someState: {
      invoke: {
        src: 'someActor',
        input: ({ event }) => {
          // [!code highlight:1]
          assertEvent(event, 'messageSent');

          return { message: event.message };
        },
      },
    },
  },
});
```

## Type helpers

XState provides some type helpers to make it easier to work with types in TypeScript.

### `ActorRefFrom<T>`

Results in an `ActorRef` from the provided `T` actor logic parameter, which is useful for creating strongly-typed actors. The `T` parameter can be any `ActorLogic`, such as the return value of `createMachine(…)`, or any other actor logic, such as `fromPromise(…)` or `fromObservable(…)`.

```ts
import { type ActorRefFrom } from 'xstate';
import { someMachine } from './someMachine';

type SomeActorRef = ActorRefFrom<typeof someMachine>;
```

### `SnapshotFrom<T>`

Results in a `Snapshot` from the provided `T` parameter, which is useful for creating strongly-typed snapshots. The `T` parameter can be any `ActorLogic` or `ActorRef`.

```ts
import { type SnapshotFrom } from 'xstate';
import { someMachine } from './someMachine';

type SomeSnapshot = SnapshotFrom<typeof someMachine>;
```

### `EventFromLogic<T>`

Results in an union of all event types defined in the provided `T` actor logic parameter. Useful for type-safe event handling.

```ts
import { type EventFromLogic } from 'xstate';
import { someMachine } from './someMachine';

// SomeEvent would be a union of all event
// types defined in `someMachine`.
type SomeEvent = EventFromLogic<typeof someMachine>;
```

## Typegen

[Typegen](/docs/developer-tools#xstate-typegen-files) is not supported in XState version 5. However, with the `setup(...)` function and/or the `.types` property explained above, you can provide strong typing for most (if not all) of your machine.

If you were previously using typegen to narrow down events used in actions or guards, you can use [the `assertEvent(...)` helper function](#asserting-events) to narrow down the event type.


# Upgrade your Stately Studio account (/docs/upgrade)





import { CircleDollarSign, ThemedImage } from 'lucide-react';

# Upgrade

**Stately Studio will always be free to our Community users** on the [Community plan](studio-community-plan), and we will make many future features available on every plan.

We offer a **free trial** on the Stately Studio [Pro](studio-pro-plan), [Team](studio-team-plan), or [Enterprise](studio-enterprise-plan) plans so you can explore how our premium features work for you and your team. You can [upgrade](upgrade) when you’re signed into Stately Studio.

## Upgrade to a Pro or Team plan

You can [upgrade](upgrade) when you’re signed into Stately Studio using the **Upgrade** button in the editor’s footer.

{/* <p>
  <ThemedImage
    alt="Upgrade button in the header of Stately Studio between the Save and share buttons."
    sources={{
      light: '/assets/upgrade/upgrade.png',
      dark: '/assets/upgrade/upgrade-dm.png',
    }}
  />
  </p> */}

<img alt="Upgrade button in the header of Stately Studio between the Save and share buttons." src={__img0} placeholder="blur" />

## Upgrade to an Enterprise plan

<p>
  <a href="mailto:support@stately.ai?subject=I'm interested in Stately Studio Enterprise plan">
    Email the Stately team
  </a>

  {' '}

  for a custom plan tailored to the requirements of your organization.
</p>

## Manage your subscription plan

* From the editor, use the Editor menu, and from the **Account** section, select <CircleDollarSign size={18} /> **Billing**.
* From elsewhere in the Studio, select your avatar from the header and choose <CircleDollarSign size={18} /> **Billing** from the account menu.

{/* <p style={{ 'max-width': '500px' }}>
  <ThemedImage
    alt="Billing option in the user account menu in the Stately Studio header."
    sources={{
      light: '/assets/upgrade/billing.png',
      dark: '/assets/upgrade/billing-dm.png',
    }}
  />
  </p> */}

<img alt="Billing option in the user account menu in the Stately Studio header." src={__img1} placeholder="blur" />


# Share machines using their share URL (/docs/url)

You can share machines with your team or publicly with the machine URL.

Your machine will only be viewable if:

* the project visibility is **public**
* the project visibility is **unlisted**
* the project visibility is **private**, and the viewer is also a member of your team

Private machines stored in My Projects will only be viewable by you.

Read [how to change a project’s visibility settings](projects.mdx#change-a-projects-visibility).

## Copy the URL from the browser address bar

You can copy the machine URL from the browser address bar, which will share the machine in your current view and version.

## Copy the URL from the Share modal

1. Use the **Share** button in Stately Studio’s top bar to open the Share dialog.
2. Use the **Copy share URL** button to copy the machine’s URL to your clipboard.

{/* TODO: add image illustrating above */}

## Machine URL parameters

Depending on where you copy your URL from, it may contain the following parameters:

* **machineId**: the unique ID for the machine. For example, `machineId=491a4c60-5300-4e22-92cf-8a32a8ffffca`.
* **mode**: the current machine mode. For example, `mode=Simulate`.
* **version**: the current [machine version](versions). For example, `version=78730cff-1e90-4190-9e41-426733b71c9f`.


# User preferences (/docs/user-preferences)

import { Sun, Moon, Sparkle } from 'lucide-react';

We have some simple user preferences to help you customize Stately Studio to fit your needs.

## Light mode and dark mode

You can choose whether you prefer light mode or dark mode, and Stately Studio will remember your setting. You must be signed in to choose your preferred mode.

<EmbedMachine name="Light mode and dark mode machine" embedURL="https://stately.ai/registry/editor/embed/727e0f1f-2f71-447c-a3e5-de435d73480b?machineId=e99b8c7d-7dc3-44a5-a8d3-984223ff33f7" />

Switch between the light and dark mode setting here in the docs to preview the machine, above, switching between light and dark mode in Stately Studio.

### Switch between light and dark mode

1. Use your avatar button in the top right of Stately Studio to open the user menu.
2. Use the setting to either <Sun size={18} /> **Switch to light mode** or <Moon size={18} /> **Switch to dark mode**.

<Callout>
  We plan to set light mode and dark mode according to your operating system preferences in the future. Please [upvote this feature in our roadmap](https://feedback.stately.ai/editor/p/automatic-lightdark-mode-based-on-system-preferences) if it’s a priority for you.
</Callout>

## Translucency

Events are translucent in the Stately Studio canvas to make it easier to distinguish overlapping events and transitions. You can choose to turn translucency off if you prefer.

### Enable and disable translucency

1. Use your avatar button in the top right of Stately Studio to open the user menu.
2. Use the setting to either <Sparkle size={18} /> **Disable translucency** or <Sparkle size={18} /> **Enable translucency**.

Translucency is not yet supported in Safari.


# Version history (/docs/versions)

import { History, ArchiveRestore, X, Copy, Trash, Info } from 'lucide-react';

Version history is helpful if you want to save checkpoints while modeling machines or mirror version changes in the rest of your codebase. New versions can be saved from the current machine using the **Version history** panel in the lower left of the canvas.

All users can view versions of public machines. Users on our premium plans can view and create versions for machines in team projects and their own machines in **My Projects**. [Read more about team roles and versions](#team-roles-and-versions).

<Callout>
  Version history is a premium feature of Stately Studio. You can try Stately Studio’s premium plans with a free trial. [Check out the features on our Pro plan](studio-pro-plan), [Team plan](studio-team-plan), [Enterprise plan](studio-enterprise-plan) or [upgrade your existing plan](https://stately.ai/registry/billing).
</Callout>

## Save a version

Save a new version of your machine from the <History size={18} /> **Version history** panel in the lower left corner of the canvas.

<Callout>
  The **Save** button will be disabled unless you have changed your machine since you created the latest version.
</Callout>

Versions are named sequentially by default and have the version creator’s avatar. The version’s timestamp is always shown under the version name.

### Rename a version

You can rename any version after double-clicking or focusing on the version name.

## Auto-saved versions

There are some cases where versions will automatically be created for a machine Auto-saved versions are indicated in the list of versions with the Stately bot icon.

### Initial versions

An initial version is automatically saved for new machines when:

* you fork a machine from an existing machine or project
* you duplicate an existing machine in its project

These versions are labeled “Initial version”.

### Generated versions

An version is also automatically saved for machines when you generate a flow from your existing machine. These versions are labeled with the text description used to generate the flow.

### Periodic snapshot versions

To reduce the likelihood of data loss, versions are automatically saved hourly as long as edits have been made to the project machine. These versions are labeled sequentially. They work just like other versions that you can view, restore, or delete.

In the version list, these auto-saved versions are hidden by default but you can toggle their visibility to show them along with versions you explicitly save.

## View a version

You can view any version by selecting that version in the **Version history**. The version will be shown in the Editor and read-only unless you restore that version using the <ArchiveRestore size={18} /> **Restore** button.

You can return to viewing the current version of your machine from the <X size={18} /> close icon button in the top bar or by selecting **Current Version** from the **Version history** panel.

## Restore a version

You can restore your current machine to any version from the <History size={18} /> **Version history** panel using the <ArchiveRestore size={18} /> restore icon button or using the <ArchiveRestore size={18} /> **Restore** button in the top bar when [viewing a version](#view-a-version).

<Callout>
  You cannot restore a version when a machine is [locked](lock-machines). Unlock the machine from the machine <Info size={18} /> **Details** panel to re-enable restoring versions.
</Callout>

## Create a new machine from a version

You can also create a new machine from a version in your version history using the <Copy size={18} /> copy button in the top bar when [viewing a version](#view-a-version).

Your new machine will be created and opened in the same project with the same name appended with “(copy).”

## Delete a version

Delete a machine version using the <Trash size={18} /> trash button in the top bar when [viewing a version](#view-a-version).

## Team roles and versions

All team members can view past versions of machines in their team’s projects. Team owners, Admins, and Editors can also create and restore versions. Only Owners and Admins can delete versions.

| Capability                           | Owner | Admin | Editor | Viewer |
| ------------------------------------ | ----- | ----- | ------ | ------ |
| View team machine versions           | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| Create new versions of team machines | ✅ Yes | ✅ Yes | ✅ Yes  | ✅ Yes  |
| Restore versions of team machines    | ✅ Yes | ✅ Yes | ✅ Yes  | 🚫 No  |
| Delete versions of team machines     | ✅ Yes | ✅ Yes | 🚫 No  | 🚫 No  |


# Visualizer (deprecated) (/docs/visualizer)



<Callout>
  Are you looking for the *Stately Studio* visual *editor*? Check out the [Stately Studio overview](/).
</Callout>

<img alt="Stately Visualizer with a split screen. The left side shows a visualized machine for a video player. The right side shows the JavaScript used to create the visualized machine." src={__img0} placeholder="blur" />

The [Stately Visualizer](https://stately.ai/viz) is a tool for creating and inspecting statecharts to visualize the state of your applications. You can use the viz as a playground for exploring XState’s capabilities.

<Callout type="warning">
  The XState Visualizer is deprecated and is no longer maintained.
</Callout>

We recommend you only continue using the legacy visualizer if you need to execute actions and guards or visualize multiple machines simultaneously. These features are coming to the [Stately editor](studio) very soon.

## Use the Visualizer

* [Take me to the Visualizer](https://stately.ai/viz)
* [Try the Visualizer in Inspect mode](https://stately.ai/viz?inspect).

The Visualizer is deprecated and we encourage you to migrate your projects to use the [Stately editor](studio).

## Feedback and bug reports

If you have any feedback or feature requests, please [join our Discord server](https://discord.gg/xstate), where you’ll find our team and the wonderful Stately community.

Please [submit any bug reports as GitHub issues on the XState repository](https://github.com/statelyai/xstate/issues).


# @xstate/fsm (/docs/xstate-fsm)

<Callout type="warning">
  The [@xstate/fsm package](https://github.com/statelyai/xstate/tree/main/packages/xstate-fsm) is deprecated in XState v5. Use [XState](https://github.com/statelyai/xstate) instead.
</Callout>

[Read the @xstate/fsm docs for XState v4](/docs/xstate-v4/xstate/packages/xstate-fsm).


# @xstate/graph (/docs/xstate-graph)

The [@xstate/graph package](https://github.com/statelyai/xstate/tree/main/packages/xstate-graph) contains graph algorithms and utilities for XState machines.

## Quick start

1. Install `xstate` and `@xstate/graph`:

```bash
npm install xstate @xstate/graph
```

2. Import the graph utilities. Example:

```js
import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';

const machine = createMachine(/* ... */);
const paths = getSimplePaths(machine);
```

## API

### `getShortestPaths(machine, options?)`

#### Arguments

* `machine` - the [`Machine`](https://xstate.js.org/docs/guides/machines.html) to traverse
* `options` (optional) - [options](#options) that customize how the algorithm will traverse the machine

Returns the [shortest paths (Dijkstra's algorithm)](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) of a [machine](https://xstate.js.org/docs/guides/machines.html) from the initial state to every other state as a mapped object, where the:

* **key** is the stringified state
* **value** is an object with the properties:
  * `state` - the target [`State`](https://xstate.js.org/docs/guides/states.html)
  * `path` - the shortest path to get from the initial state to the target state

The `path` is an array of segments, where each segment is an object with the properties:

* `state` - the [`State`](https://xstate.js.org/docs/guides/states.html) of the segment
* `weight` - the total [weight](https://en.wikipedia.org/wiki/Graph_\(discrete_mathematics\)#Weighted_graph) of the path
  * Currently, each transition from one state to another has a weight of 1. This will be customizable in the future.
* `event` - the event object that transitions the `machine` from the state to the next state in the path

Every path starts with the initial state.

The overall object structure looks like this:

```json5
{
  "<SERIALIZED STATE>": {
    "state": State { ... },
    "path": [
      {
        "state": State { ... },
        "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
      },
      {
        "state": State { ... },
        "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
      },
      ...
    ]
  },
  ...
}
```

#### Example

```js
import { createMachine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    closed: {
      type: 'final',
    },
  },
});

const shortestPaths = getShortestPaths(feedbackMachine);

console.log(shortestPaths);
// => {
//   '"question"': {
//     state: State { value: 'question', context: undefined },
//     weight: 0,
//     path: []
//   },
//   '"thanks"': {
//     state: State { value: 'thanks', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLICK_GOOD' }
//       }
//     ]
//   },
//   '"form"': {
//     state: State { value: 'form', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLICK_BAD' }
//       }
//     ]
//   },
//   '"closed"': {
//     state: State { value: 'closed', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLOSE' }
//       }
//     ]
//   }
// };
```

### `getSimplePaths(machine, options?)`

Arguments:

* `machine` - the [`Machine`](https://xstate.js.org/docs/guides/machines.html) to traverse
* `options` (optional) - [options](#options) that customize how the algorithm will traverse the machine

Returns the [simple paths](https://en.wikipedia.org/wiki/Path_\(graph_theory\)#Definitions) of a [machine](https://xstate.js.org/docs/guides/machines.html) as a mapped object, where the:

* **key** is the stringified state
* **value** is an object with the properties:
  * `state` - the target [`State`](https://xstate.js.org/docs/guides/states.html)
  * `paths` - the array of paths to get from the initial state to the target state

Each `path` in `paths` is an array of segments, where each segment of the path is an object with the properties:

* `state` - the [`State`](https://xstate.js.org/docs/guides/states.html) of the segment
* `event` - the event object that transitions the `machine` from the state to the next state in the path

Every path starts with the initial state.

The overall object structure looks like this:

```json5
{
  "<SERIALIZED STATE>": {
    "state": State { ... },
    "paths": [
      [
        {
          "state": State { ... },
          "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
        },
        {
          "state": State { ... },
          "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
        },
        ...
      ],
      ...
    ]
  },
  ...
}
```

#### Example

```js
import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    closed: {
      type: 'final',
    },
  },
});

const simplePaths = getSimplePaths(feedbackMachine);

console.log(simplePaths);
// => {
//   '"question"': {
//     state: { value: 'question', context: undefined },
//     paths: [[]]
//   },
//   '"thanks"': {
//     state: { value: 'thanks', context: undefined },
//     paths: [
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         }
//       ],
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_BAD' }
//         },
//         {
//           state: { value: 'form', context: undefined },
//           event: { type: 'SUBMIT' }
//         }
//       ]
//     ]
//   },
//   '"closed"': {
//     state: { value: 'closed', context: undefined },
//     paths: [
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         },
//         {
//           state: { value: 'thanks', context: undefined },
//           event: { type: 'CLOSE' }
//         }
//       ],
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         },
//         {
//           state: { value: 'thanks', context: undefined },
//           event: { type: 'ESC' }
//         }
//       ],
//       ...
//     ]
//   },
//   ...
// };
```

### `getPathFromEvents(machine, events)`

Arguments:

* `machine` - the [`Machine`](https://xstate.js.org/docs/guides/machines.html) to traverse
* `events` - the sequence of events to generate a path from

Returns a path object with the following keys:

* `state` - the target [`State`](https://xstate.js.org/docs/guides/states.html)
* `segments` - an array of objects with the following shape:
  * `state` - the [`State`](https://xstate.js.org/docs/guides/states.html) of the segment
  * `event` - the event object that transitions the `machine` from the state to the next state in the path

```js
import { createMachine } from 'xstate';
import { getPathsFromEvents } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed',
      },
    },
    closed: {
      type: 'final',
    },
  },
});

const path = getPathsFromEvents(feedbackMachine, [
  { type: 'CLICK_GOOD' },
  { type: 'SUBMIT' },
  { type: 'CLOSE' },
]);

console.log(path);
// => {
//   state: { value: 'closed' },
//   segments: [
//     {
//       state: { value: 'question' },
//       event: { type: 'CLICK_GOOD' },
//     },
//     {
//       state: { value: 'form' },
//       event: { type: 'SUBMIT' },
//     },
//     {
//       state: { value: 'thanks' },
//       event: { type: 'CLOSE' },
//     },
//   ],
// }
```

### `toDirectedGraph(machine)`

Converts a `machine` to a directed graph structure.

| Argument  | Type                                           | Description                                          |
| --------- | ---------------------------------------------- | ---------------------------------------------------- |
| `machine` | XState Machine created by `createMachine(...)` | The machine to convert to a directed graph structure |

#### Example

```js
import { toDirectedGraph } from '@xstate/graph';

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

const digraph = toDirectedGraph(machine);

// returns an object with this structure:
{
  id: '...',
  stateNode: /* StateNode */,
  children: [
    { id: '...', children: [/* ... */], edges: [/* ... */] },
    { id: '...', /* ... */ },
    // ...
  ],
  edges: [
    { source: /* ... */, target: /* ... */, transition: /* ... */ }
    // ...
  ]
}
```

## Options

Options can be passed into `getShortestPaths` or `getSimplePaths` to customize how the graph represented by the machine should be traversed:

* `events` - a mapping of event types to an array of event objects to be used for those events
* `filter` - a function that determines whether a `state` should be traversed. If `false`, the traversal algorithm(s) will assume the state was "seen" and ignore traversing it.

#### Examples

In the below example, the `INC` event is expanded to include two possible events, with `value: 1` and `value: 2` as the payload. It also ensures that the `state.context.count <= 5`; otherwise, this machine would be traversed infinitely.

```js
const counterMachine = createMachine({
  id: 'counter',
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      on: {
        INC: {
          actions: assign({ count: (ctx, e) => ctx.count + e.value }),
        },
      },
    },
  },
});

const shortestPaths = getShortestPaths(counterMachine, {
  events: {
    INC: [
      { type: 'INC', value: 1 },
      { type: 'INC', value: 2 },
    ],
  },
  filter: (state) => state.context.count <= 5,
});

console.log(shortestPaths);
// => {
//   '"active" | {"count":0}': {
//     state: { value: 'active', context: { count: 0 } },
//     weight: 0,
//     path: []
//   },
//   '"active" | {"count":1}': {
//     state: { value: 'active', context: { count: 1 } },
//     weight: 1,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 1 }
//       }
//     ]
//   },
//   '"active" | {"count":2}': {
//     state: { value: 'active', context: { count: 2 } },
//     weight: 1,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 2 }
//       }
//     ]
//   },
//   '"active" | {"count":3}': {
//     state: { value: 'active', context: { count: 3 } },
//     weight: 2,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 1 }
//       },
//       {
//         state: { value: 'active', context: { count: 1 } },
//         event: { type: 'INC', value: 2 }
//       }
//     ]
//   },
//   ...
// };
```


# @xstate/immer (/docs/xstate-immer)

<Callout type="warning">
  The [@xstate/immer package](https://github.com/statelyai/xstate/tree/main/packages/xstate-immer) is deprecated in XState v5.
</Callout>

[Read the @xstate/immer docs for XState v4](/docs/xstate-v4/xstate/packages/xstate-immer).


# @xstate/react (/docs/xstate-react)

The [@xstate/react package](https://github.com/statelyai/xstate/tree/main/packages/xstate-react) contains hooks and helper functions for using [XState](https://github.com/statelyai/xstate) with [React](https://github.com/facebook/react/).

## Templates

Use the following templates to get started quickly with XState and React:

* [XState React template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/react-ts)
* [XState React template (StackBlitz)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/react-ts?file=%2Fsrc%2FfeedbackMachine.ts)

## Installation

Install the latest versions of both `xstate` and `@xstate/react`. `xstate` is a peer dependency of `@xstate/react`.

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate @xstate/react
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate @xstate/react
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate @xstate/react
    ```
  </Tab>

  <Tab value="bun" label="bun">
    ```bash
    bun add xstate @xstate/react
    ```
  </Tab>
</Tabs>

<Callout>
  Want to get started with React extra fast? Try the [generate React app feature in Stately Studio](generate-react).
</Callout>

## Examples

See the React examples in the [XState GitHub repo](https://github.com/statelyai/xstate/tree/main/examples).

## API

### `useActor(actorLogic, options?)`

A [React hook](https://reactjs.org/hooks) that creates an actor from the the given `actorLogic` and starts an actor that runs for the lifetime of the component.

#### Arguments

* `actorLogic` - The actor logic to create an actor from; e.g. `createMachine(...)`, `fromPromise(...)`, etc.
* `options?` (optional) - Actor options.

**Returns** a tuple of `[snapshot, send, actorRef]`:

* `snapshot` - Represents the current state of the actor.
* `send` - A function that sends events to the running actor.
* `actorRef` - The started actor.

#### Examples

```tsx
import { fromPromise } from 'xstate';
import { useActor } from '@xstate/react';

const promiseLogic = fromPromise(async () => {
  const data = await getData(/* ... */);

  return data;
});

function Component() {
  // [!code highlight:1]
  const [state, send] = useActor(promiseLogic);

  if (state.status === 'done') {
    return <div>{state.output}</div>;
  }

  if (state.status === 'active') {
    return <div>Loading...</div>;
  }

  return null;
}
```

### `useMachine(machine, options?)`

A [React hook](https://reactjs.org/hooks) that creates an actor from the given `machine` and starts an actor that runs for the lifetime of the component.

<Callout>
  The `useMachine(...)` hook is an alias for the `useActor(...)` hook.
</Callout>

#### Arguments

* `machine` - An [XState machine](machines)
* `options?` (optional) - Actor options.

**Returns** a tuple of `[snapshot, send, actorRef]`:

* `snapshot` - Represents the current state of the machine.
* `send` - A function that sends events to the running actor.
* `actorRef` - The started actor.

#### Examples

```tsx
import { useMachine } from '@xstate/react';

function Component() {
  const [snapshot, send] = useMachine(machine);

  // Machine with provided implementations
  // Will keep provided implementations up-to-date
  const [snapshot, send] = useMachine(
    machine.provide({
      actions: {
        doSomething: ({ context }) => {
          // ...
        },
      },
    }),
  );
}
```

### `useActorRef(machine, options?)`

A React hook that returns the `actorRef` created from the `machine` with actor `options` that are passed to `createActor(logic, options)`, if specified. It starts the actor ref and runs it for the lifetime of the component.

The `useActorRef(...)` hook is useful when you want fine-grained control, e.g. to add logging, or minimize re-renders. In contrast to `useActor(...)` that would flush each update from the machine to the React component, `useActorRef(...)` instead returns a static reference (to just the machine actor) which will not rerender when its state changes.

You can use the `useSelector(...)` hook to select part of the snapshot from the `actorRef` whenever it updates.

#### Arguments

* `actorLogic`- The actor logic to create an actor from; e.g. `createMachine(...)`, `fromPromise(...)`, etc.
* `options?` (optional) - Actor options.

```js
import { useActorRef } from '@xstate/react';
import { someMachine } from '../path/to/someMachine';

const App = () => {
  const actorRef = useActorRef(someMachine);

  // ...
};
```

Providing machine implementations:

```js
// ...

const App = () => {
  const actorRef = useActorRef(
    someMachine.provide({
      actions: {
        // ...
      },
    }),
  );

  // ...
};
```

### `useSelector(actorRef, selector, compare?, getSnapshot?)`

A React hook that returns the selected value from the snapshot of an `actorRef`, such as a actor ref. This hook will only cause a rerender if the selected value changes, as determined by the optional `compare` function.

#### Arguments

* `actorRef` - an actor ref
* `selector` - a function that takes in an actor’s snapshot as an argument and returns the desired selected value.
* `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.
* `getSnapshot` (optional) - a function that should return the latest emitted value from the `actor`.
  * Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist. Will automatically pull the state from actor refs.

#### Examples

```js
import { useSelector } from '@xstate/react';

// tip: optimize selectors by defining them externally when possible
const selectCount = (snapshot) => snapshot.context.count;

const App = ({ actorRef }) => {
  const count = useSelector(actorRef, selectCount);

  // ...
};
```

With `compare` function:

```js
// ...

const selectUser = (snapshot) => snapshot.context.user;
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;

const App = ({ actorRef }) => {
  const user = useSelector(actorRef, selectUser, compareUser);

  // ...
};
```

### `createActorContext(logic)`

Returns a [React Context object](https://beta.reactjs.org/learn/passing-data-deeply-with-context) that creates an actor from the provided actor `logic` and makes the actor available through React Context. There are helper methods for accessing state and the actor ref.

#### Arguments

* `logic` - Actor logic, like an [XState machine](machines)

Returns a React Context object that contains the following properties:

* `Provider` - a React Context Provider component with the following props:
  * `logic` - Actor logic, such as an [XState machine](machines) ,that must be of the same type as the actor logic passed to `createActorContext(...)`
* `useSelector(selector, compare?)` - a React hook that takes in a `selector` function and optional `compare` function and returns the selected value from the actor snapshot
* `useActorRef()` - a React hook that returns the actor ref of the actor created from the actor `logic`

Creating a React Context for the actor and providing it in app scope:

```js
import { createActorContext } from '@xstate/react';
import { someMachine } from '../path/to/someMachine';

const SomeMachineContext = createActorContext(someMachine);

function App() {
  return (
    <SomeMachineContext.Provider>
      <SomeComponent />
    </SomeMachineContext.Provider>
  );
}
```

Consuming the actor in a component:

```js
import { SomeMachineContext } from '../path/to/SomeMachineContext';

function SomeComponent() {
  const count = SomeMachineContext.useSelector((state) => state.context.count);
  const someActorRef = SomeMachineContext.useActorRef();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => someActorRef.send({ type: 'inc' })}>
        Increment
      </button>
    </div>
  );
}
```

Providing a similar machine:

```js
import { SomeMachineContext } from '../path/to/SomeMachineContext';
import { someMachine } from '../path/to/someMachine';

function SomeComponent() {
  return (
    <SomeMachineContext.Provider
      logic={someMachine.provide({
        actions: {
          someAction: differentImplementation,
        },
        // ... More implementations
      })}
    >
      <SomeOtherComponent />
    </SomeMachineContext.Provider>
  );
}
```

### Shallow comparison

The default comparison is a strict reference comparison (`===`). If your selector returns non-primitive values, such as objects or arrays, you should keep this in mind and either return the same reference, or provide a shallow or deep comparator.

The `shallowEqual(...)` comparator function is available for shallow comparison:

```js
import { useSelector, shallowEqual } from '@xstate/react';

// ...

const selectUser = (state) => state.context.user;

const App = ({ actorRef }) => {
  // shallowEqual comparator is needed to compare the object, whose
  // reference might change despite the shallow object values being equal
  const user = useSelector(actorRef, selectUser, shallowEqual);

  // ...
};
```

With `useActorRef(...)`:

```js
import { useActorRef, useSelector } from '@xstate/react';
import { someMachine } from '../path/to/someMachine';

const selectCount = (state) => state.context.count;

const App = () => {
  const actorRef = useActorRef(someMachine);
  const count = useSelector(actorRef, selectCount);

  // ...
};
```

## Configuring machines

Existing machines can be customized by providing different implementations in `machine.provide(implementations)`.

Example: the `'fetchData'` actor ref and `'notifySuccess'` action are both configurable:

```js
const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  context: {
    data: undefined,
    error: undefined,
  },
  states: {
    idle: {
      on: { FETCH: 'loading' },
    },
    loading: {
      invoke: {
        src: 'fetchData',
        onDone: {
          target: 'success',
          actions: assign({
            data: ({ event }) => event.output,
          }),
        },
        onError: {
          target: 'failure',
          actions: assign({
            error: ({ event }) => event.error,
          }),
        },
      },
    },
    success: {
      entry: 'notifySuccess',
      type: 'final',
    },
    failure: {
      on: {
        RETRY: 'loading',
      },
    },
  },
});

const Fetcher = ({ onResolve }) => {
  const [state, send] = useMachine(
    fetchMachine.provide({
      actions: {
        notifySuccess: ({ context }) => onResolve(context.data),
      },
      actors: {
        fetchData: fromPromise(() =>
          fetch(`some/api/${e.query}`).then((res) => res.json()),
        ),
      },
    }),
  );

  switch (state.value) {
    case 'idle':
      return (
        <button onClick={() => send({ type: 'FETCH', query: 'something' })}>
          Search for something
        </button>
      );
    case 'loading':
      return <div>Searching...</div>;
    case 'success':
      return <div>Success! Data: {state.context.data}</div>;
    case 'failure':
      return (
        <>
          <p>{state.context.error.message}</p>
          <button onClick={() => send({ type: 'RETRY' })}>Retry</button>
        </>
      );
    default:
      return null;
  }
};
```

## Input

You can provide input to actors

## Matching states

When using [hierarchical](parent-states) and [parallel](parallel-states) machines, the state values will be objects, not strings. In this case, it is best to use [`state.matches(...)`](states.mdx#statematchesstatevalue).

We can do this with `if/else if/else` blocks:

```js
// ...
if (state.matches('idle')) {
  return /* ... */;
} else if (state.matches({ loading: 'user' })) {
  return /* ... */;
} else if (state.matches({ loading: 'friends' })) {
  return /* ... */;
} else {
  return null;
}
```

We can also continue to use `switch`, but we must make an adjustment to our approach. By setting the expression of the `switch` to `true`, we can use [`state.matches(...)`](states.mdx#statematchesstatevalue) as a predicate in each `case`:

```js
switch (true) {
  case state.matches('idle'):
    return /* ... */;
  case state.matches({ loading: 'user' }):
    return /* ... */;
  case state.matches({ loading: 'friends' }):
    return /* ... */;
  default:
    return null;
}
```

A ternary statement can also be considered, especially within rendered JSX:

```jsx
const Loader = () => {
  const [state, send] = useMachine(/* ... */);

  return (
    <div>
      {state.matches('idle') ? (
        <Loader.Idle />
      ) : state.matches({ loading: 'user' }) ? (
        <Loader.LoadingUser />
      ) : state.matches({ loading: 'friends' }) ? (
        <Loader.LoadingFriends />
      ) : null}
    </div>
  );
};
```

## Persisted and rehydrated State

You can persist and rehydrate state with `useMachine(...)` via `options.snapshot` in the 2nd argument:

```js
// ...

// Get the persisted state config object from somewhere, e.g. localStorage
const persistedState = JSON.parse(localStorage.getItem('some-persisted-state-key'));

const App = () => {
  const [state, send] = useMachine(someMachine, {
    snapshot: persistedState // provide persisted state config object here
  });

  // state will initially be that persisted state, not the machine’s initialState

  return (/* ... */)
}
```

## Actor refs

The `actorRef` created in `useMachine(machine)` can be referenced as the third returned value:

```js
//                  vvvvvvvv
const [state, send, actorRef] = useMachine(someMachine);
```

You can subscribe to that actor ref's state changes with the [`useEffect` hook](https://reactjs.org/docs/hooks-effect.html):

```js
// ...

useEffect(() => {
  const subscription = actorRef.subscribe((snapshot) => {
    // simple logging
    console.log(snapshot);
  });

  return subscription.unsubscribe;
}, [actorRef]); // note: actor ref should never change
```

## Resources

*Coming soon*


# @xstate/store (/docs/xstate-store-v2)

**Version 2.x** ([Version 3.x docs](./xstate-store))

XState Store is a small library for simple state management in JavaScript/TypeScript applications. It is meant for updating store data using **events** for vanilla JavaScript/TypeScript apps, React apps, and more. It is comparable to libraries like Zustand, Redux, and Pinia. For more complex state management, you should use [XState](./xstate) instead, or you can [use XState Store with XState](#using-xstate-store-with-xstate).

<Callout>
  The `@xstate/store` library requires TypeScript version 5.4 or above.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store
    ```
  </Tab>
</Tabs>

## Quick start

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  // Initial context
  context: { count: 0, name: 'David' },
  // Transitions
  on: {
    inc: (context) => ({
      count: context.count + 1,
    }),
    add: (context, event: { num: number }) => ({
      count: context.count + event.num,
    }),
    changeName: (context, event: { newName: string }) => ({
      name: event.newName,
    }),
  },
});

// Get the current state (snapshot)
console.log(store.getSnapshot());
// => {
//   status: 'active',
//   context: { count: 0, name: 'David' }
// }

// Subscribe to snapshot changes
store.subscribe((snapshot) => {
  console.log(snapshot.context);
});

// Send an event
store.send({ type: 'inc' });
// logs { count: 1, name: 'David' }

store.send({ type: 'add', num: 10 });
// logs { count: 11, name: 'David' }

store.send({ type: 'changeName', newName: 'Jenny' });
// logs { count: 11, name: 'Jenny' }
```

## Creating a store

To create a store, you need to pass an object to the `createStore(…)` function with the following properties:

1. The initial `context`
2. An `on` object for transitions (event handlers) where:

* The keys are the event types (e.g. `"inc"`, `"add"`, `"changeName"`)
* The values are the `context` updates to apply when the event is sent to the store, as either an object or a function.

Updating `context` in transitions is similar to using the [`assign` action](./context) in XState. You can update specific `context` properties by using an object:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0, incremented: false /* ... */ },
  on: {
    // [!code highlight:5]
    inc: {
      count: (context, event: { by: number }) => context.count + event.by,
      // Static values do not need to be wrapped in a function
      incremented: true,
    },
  },
});
```

Or you can update the entire `context` by using a function:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0, incremented: false /* ... */ },
  on: {
    // [!code highlight:8]
    inc: (context, event: { by: number }) => {
      // ...

      return {
        count: context.count + event.by,
        incremented: true,
      };
    },
  },
});
```

You can spread the `...context` when updating the entire `context` with a function. This is useful when you want to preserve other properties in the `context`:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0, incremented: false /* ... */ },
  on: {
    reset: (context, event) => {
      // [!code highlight:5]
      // You can use `...context` to preserve other properties
      return {
        ...context,
        count: 0,
      };
    },
  },
});
```

<details>
  <summary>
    Note: Deprecated <code>createStore(context, transitions)</code> API
  </summary>

  The previous version of `createStore` took two arguments: an initial context and an object of event handlers. This API is still supported but deprecated. Here's an example of the old usage:

  ```ts
  import { createStore } from '@xstate/store';

  const donutStore = createStore(
    {
      donuts: 0,
      favoriteFlavor: 'chocolate',
    },
    {
      addDonut: (context) => ({ ...context, donuts: context.donuts + 1 }),
      changeFlavor: (context, event: { flavor: string }) => ({
        ...context,
        favoriteFlavor: event.flavor,
      }),
      eatAllDonuts: (context) => ({ ...context, donuts: 0 }),
    },
  );
  ```

  We recommend using the new API for better type inference and more explicit configuration.
</details>

## Transition functions

A transition function is a function that takes the current `context` and an `event` object, and returns:

* The partial or entire `context` object to update (if using a function assigner)
* The context property value to update (if using an object assigner).

For strong typing, you should specify the payload type of the `event` object in the transition function.

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { name: 'David', count: 0 },
  on: {
    // [!code highlight:10]
    updateName: (context, event: { name: string }) => {
      return {
        name: event.name,
      };
    },
    inc: {
      count: (context, event: { by: number }) => {
        return context.count + event.by;
      },
    },
  },
});

store.send({
  type: 'updateName',
  name: 'Jenny', // Strongly-typed as `string`
});

store.send({
  type: 'inc',
  by: 10, // Strongly-typed as `number`
});
```

## Emitting events

You can emit events from transitions by using the `emit` method from the 3rd argument of the transition function:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  types: {
    // [!code highlight:1]
    emitted: {} as { type: 'incremented'; by: number },
  },
  context: { count: 0 },
  on: {
    // [!code highlight:1]
    inc: (context, event: { by: number }, { emit }) => {
      if (event.by > 0) {
        // [!code highlight:1]
        emit({ type: 'incremented', by: event.by });
      }

      return {
        count: context.count + event.by,
      };
    },
  },
});

// [!code highlight:4]
const sub = store.on('incremented', (event) => {
  console.log(`Emitted by ${event.by}`);
  // => logs "Emitted by 10"
});

store.send({ type: 'inc', by: 10 });

// Stop listening for emitted events
// [!code highlight:1]
sub.unsubscribe();
```

You can listen for emitted events using the `store.on(...)` method, which creates a subscription that you can later unsubscribe from. This method is type-safe, ensuring that you receive the correct event object for the emitted event type you're listening for.

Note that you can strongly type emitted events in the `types.emitted` property of the store config object, just like in XState. This ensures type safety when emitting and listening for events.

## Inspection

Just like with XState, you can use the [Inspect API](./inspection) to inspect events sent to the store and state transitions within the store by using the .inspect method:

```ts
const store = createStore({
  // ...
});

// [!code highlight:5]
store.inspect((inspectionEvent) => {
  // type: '@xstate.snapshot' or
  // type: '@xstate.event'
  console.log(inspectionEvent);
});
```

<Callout>
  Since the store is automatically started, inspectors will immediately receive the initial state snapshot.
</Callout>

The `.inspect(…)` method returns a subscription object:

```ts
const sub = store.inspect((inspectionEvent) => {
  console.log(inspectionEvent);
});

// Stop listening for inspection events
sub.unsubscribe();
```

You can use the [Stately Inspector](./inspector) to inspect and visualize the state of the store.

```ts
// [!code highlight:1]
import { createBrowserInspector } from '@statelyai/inspect';
import { createStore } from '@xstate/store';

const store = createStore({
  // ...
});

// [!code highlight:3]
const inspector = createBrowserInspector({
  // ...
});

// [!code highlight:1]
store.inspect(inspector);
```

## Using Immer

If you want to use [Immer](https://immerjs.github.io/immer/) to update the `context`, you can do so by passing in the `produce` function as the first argument to `createStoreWithProducer(producer, …)`.

```ts
import { createStoreWithProducer } from '@xstate/store';
// [!code highlight:1]
import { produce } from 'immer';

const store = createStoreWithProducer(
  // [!code highlight:2]
  // Producer
  produce,
  {
    context: { count: 0, todos: [] },
    on: {
      inc: (context, event: { by: number }) => {
        // [!code highlight:2]
        // No return; handled by Immer
        context.count += event.by;
      },
      addTodo: (context, event: { todo: string }) => {
        // [!code highlight:2]
        // No return; handled by Immer
        context.todos.push(event.todo);
      },
    },
  },
);

// ...
```

Note that you cannot use the object assigner syntax when using `createStoreFromProducer(…)`, nor is it even necessary.

## Usage with React

If you are using React, you can use the `useSelector(store, selector)` hook to subscribe to the store and get the current state.

```tsx
import { createStore } from '@xstate/store';
// [!code highlight:1]
import { useSelector } from '@xstate/store/react';

// Create a store
const store = createStore({
  context: { count: 0, name: 'David' },
  on: {
    inc: {
      count: (context) => context.count + 1,
    },
  },
});

// Use the `useSelector` hook to subscribe to the store
function Component(props) {
  // [!code highlight:1]
  const count = useSelector(store, (state) => state.context.count);

  // This component displays the count and has a button to increment it
  return (
    <div>
      // [!code highlight:1]
      <button onClick={() => store.send({ type: 'inc' })}>Increment</button>
    </div>
  );
}
```

A store can be shared with multiple components, which will all receive the same snapshot from the store instance. Stores are useful for global state management.

## Usage with Solid

*Documentation coming soon!*

## Using XState Store with XState

You may notice that stores are very similar to [actors in XState](./actors). This is very much by design. XState's actors are very powerful, but may also be too complex for simple use cases, which is why `@xstate/store` exists.

However, if you have existing XState code, and you enjoy the simplicity of creating store logic with `@xstate/store`, you can use the `fromStore(context, transitions)` actor logic creator to create XState-compatible store logic that can be passed to the `createActor(storeLogic)` function:

```ts
// [!code highlight:2]
import { fromStore } from '@xstate/store';
import { createActor } from 'xstate';

// Instead of:
// const store = createStore( ... };
const storeLogic = fromStore({
  context: { count: 0, incremented: false /* ... */ },
  on: {
    // [!code highlight:5]
    inc: {
      count: (context, event) => context.count + 1,
      // Static values do not need to be wrapped in a function
      incremented: true,
    },
  },
});

const store = createActor(storeLogic);
store.subscribe((snapshot) => {
  console.log(snapshot);
});
store.start();

store.send({
  type: 'inc',
});
```

In short, you can convert `createStore(…)` to `fromStore(…)` just by changing one line of code. Note that `fromStore(…)` returns *store logic*, and not a store actor instance. Store logic is passed to `createActor(storeLogic)` to create a store actor instance:

```ts
// Instead of:
// const store = createStore({
const storeLogic = fromStore({
  context: {
    // ...
  },
  on: {
    // ...
  },
});

// Create the store (actor)
const storeActor = createActor(storeLogic);
```

Using `fromStore(…)` to create store actor logic also has the advantage of allowing you to provide `input` by using a context function that takes in the `input` and *returns* the initial `context`:

```ts
import { fromStore } from '@xstate/store';

// [!code highlight:5]
const storeLogic = fromStore({
  context: (initialCount: number) => ({
    count: initialCount,
  }),
  on: {
    // ...
  },
});

const actor = createActor(storeLogic, {
  // [!code highlight:1]
  input: 42,
});
```

## Converting stores to state machines

If you have a store that you want to convert to a state machine in XState, you can convert it in a straightforward way:

1. Use `createMachine(…)` (imported from `xstate`) instead of `createStore(…)` (imported from `@xstate/store`) to create a state machine.
2. Wrap the assignments in an `assign(…)` action creator (imported from `xstate`) and move that to the `actions` property of the transition.
3. Destructure `context` and `event` from the first argument instead of them being separate arguments.

For example, here is our store before conversion:

```ts
import { createMachine } from 'xstate';

// 1. Use `createMachine(…)` instead of `createStore(…)`
const store = createStore({
  context: { count: 0, name: 'David' },
  on: {
    inc: {
      // 2. Wrap the assignments in `assign(…)`
      // 3. Destructure `context` and `event` from the first argument
      count: (context, event: { by: number }) => context.count + event.by,
    },
  },
});

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

And here is the store as a state machine after conversion:

```ts
import { createMachine } from 'xstate';

// const store = createStore(
//   { count: 0, name: 'David' },
//   {
//     inc: {
//       count: (context, event: { by: number }) => context.count + event.by
//     }
//   });

// 1. Use `createMachine(…)` instead of `createStore(…)`
const machine = createMachine({
  context: {
    count: 0,
    name: 'David',
  },
  on: {
    inc: {
      // 2. Wrap the assignments in `assign(…)`
      actions: assign({
        // 3. Destructure `context` and `event` from the first argument
        count: ({ context, event }) => context.count + event.by,
      }),
    },
  },
});
```

For stronger typing, use the [`setup(…)` function](./setup) to strongly type the `context` and `events`:

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:4]
  types: {
    context: {} as { count: number; name: string },
    events: {} as { type: 'inc'; by: number },
  },
}).createMachine({
  // Same as the previous example
});
```

## Comparison

This section compares XState Store to other popular state management libraries in TypeScript. It is meant for reference purposes only, and not intended to favor one approach over the other. The examples are copied from [Zustand's comparison docs](https://docs.pmnd.rs/zustand/getting-started/comparison).

### Compare to Zustand

**Zustand**

```ts
import { create } from 'zustand';

type State = {
  count: number;
};

type Actions = {
  increment: (qty: number) => void;
  decrement: (qty: number) => void;
};

const useCountStore = create<State & Actions>((set) => ({
  count: 0,
  increment: (qty: number) =>
    set((state) => ({
      count: state.count + qty,
    })),
  decrement: (qty: number) =>
    set((state) => ({
      count: state.count - qty,
    })),
}));

const Component = () => {
  const count = useCountStore((state) => state.count);
  const increment = useCountStore((state) => state.increment);
  const decrement = useCountStore((state) => state.decrement);
  // ...
};
```

**XState Store**

```ts
import { createStore } from '@xstate/store';
import { useSelector } from '@xstate/store/react';

const store = createStore({
  context: {
    count: 0,
  },
  on: {
    increment: (context, { qty }: { qty: number }) => ({
      count: context.count + qty,
    }),
    decrement: (context, { qty }: { qty: number }) => ({
      count: context.count - qty,
    }),
  },
});

const Component = () => {
  const count = useSelector(store, (state) => state.context.count);
  const increment = (qty) => store.send({ type: 'increment', qty });
  const decrement = (qty) => store.send({ type: 'decrement', qty });
  // ...
};
```


# @xstate/store (/docs/xstate-store)

XState Store is a small library for simple state management in JavaScript/TypeScript applications. It is meant for updating store data using **events** for vanilla JavaScript/TypeScript apps, React apps, and more. It is comparable to libraries like Zustand, Redux, and Pinia. For more complex state management, you should use [XState](./xstate) instead, or you can [use XState Store with XState](#using-xstate-store-with-xstate).

<Callout>
  These are the docs for the latest version 3.x of XState Store. For version 2.x, see [Version 2.x docs](./xstate-store-v2).
</Callout>

<Callout>
  The `@xstate/store` library requires TypeScript version 5.4 or above.
</Callout>

## Framework integrations

For framework-specific usage, install the appropriate package:

* **React**: [`@xstate/store-react`](/docs/xstate-store/react)
* **Vue**: [`@xstate/store-vue`](/docs/xstate-store/vue)
* **Svelte**: [`@xstate/store-svelte`](/docs/xstate-store/svelte)
* **Solid**: [`@xstate/store-solid`](/docs/xstate-store/solid)
* **Angular**: [`@xstate/store-angular`](/docs/xstate-store/angular)
* **Preact**: [`@xstate/store-preact`](/docs/xstate-store/preact)

<Callout type="warning">
  **Deprecation notice:** Importing from `@xstate/store/react`, `@xstate/store/solid`, etc. is deprecated. Use the dedicated packages above instead (e.g., `@xstate/store-react`).
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store
    ```
  </Tab>
</Tabs>

## Quick start

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  // Initial context
  context: { count: 0, name: 'David' },
  // Transitions
  on: {
    inc: (context) => ({
      ...context,
      count: context.count + 1,
    }),
    add: (context, event: { num: number }) => ({
      ...context,
      count: context.count + event.num,
    }),
    changeName: (context, event: { newName: string }) => ({
      ...context,
      name: event.newName,
    }),
  },
});

// Get the current state (snapshot)
console.log(store.getSnapshot());
// => {
//   status: 'active',
//   context: { count: 0, name: 'David' }
// }

// Subscribe to snapshot changes
store.subscribe((snapshot) => {
  console.log(snapshot.context);
});

// Send an event (traditional way)
store.send({ type: 'inc' });
// logs { count: 1, name: 'David' }

// Send an event using the fluent trigger API
store.trigger.add({ num: 10 });
// logs { count: 11, name: 'David' }

store.trigger.changeName({ newName: 'Jenny' });
// logs { count: 11, name: 'Jenny' }
```

## Creating a store

To create a store, pass a configuration object to the `createStore(…)` function with:

1. The initial `context`
2. An `on` object for transitions where the keys are event types and the values are context update functions

When updating context in transitions, you must return the complete context object with all properties:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0, name: 'David' },
  on: {
    // [!code highlight:4]
    inc: (context) => ({
      ...context, // Preserve other context properties
      count: context.count + 1,
    }),
  },
});
```

## Effects and Side Effects

You can enqueue effects in state transitions using the `enqueue` argument:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0 },
  on: {
    // [!code highlight:9]
    incrementDelayed: (context, event, enqueue) => {
      enqueue.effect(() => {
        setTimeout(() => {
          store.send({ type: 'increment' });
        }, 1000);
      });

      return context;
    },
    increment: (context) => ({
      ...context,
      count: context.count + 1,
    }),
  },
});
```

<Callout type="warning">
  **Important:** Both `enqueue.effect()` and `enqueue.emit()` must be called **synchronously** within the transition function. Calling them outside the function's synchronous execution (e.g., inside an async callback or Promise) will have no effect, since the transition function must complete synchronously.

  For asynchronous operations, send an event back to the store:

  ```ts
  const store = createStore({
    context: { data: null },
    emits: {
      loaded: (payload: { data: string }) => {},
    },
    on: {
      fetchData: (context, event, enqueue) => {
        // ✅ This works - called synchronously
        enqueue.effect(async () => {
          const result = await fetch('/api/data');
          const data = await result.json();
          // Send event back to store for async operations
          store.trigger.dataLoaded({ data });
        });
        return context;
      },
      dataLoaded: (context, event: { data: string }, enqueue) => {
        // ✅ Now you can emit synchronously
        enqueue.emit.loaded({ data: event.data });
        return { ...context, data: event.data };
      },
    },
  });
  ```

  This ensures **determinism**: all state changes, effects, and emitted events must be the direct result of an event being sent to the store. Async callbacks cannot enqueue effects or emit events because they happen at unpredictable times, breaking the deterministic event-driven model.
</Callout>

## Emitting Events

You can emit events from transitions by defining them in the `emits` property and using `enqueue.emit`:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: { count: 0 },
  // [!code highlight:5]
  emits: {
    increased: (payload: { by: number }) => {
      // Optional side effects can go here
    },
  },
  on: {
    inc: (context, event: { by: number }, enqueue) => {
      // [!code highlight:1]
      enqueue.emit.increased({ by: event.by });

      return {
        ...context,
        count: context.count + event.by,
      };
    },
  },
});

// Listen for emitted events
store.on('increased', (event) => {
  console.log(`Count increased by ${event.by}`);
});
```

## Pure transitions

You can use `store.transition(state, event)` to compute a tuple of the next state and any effects from a given state and event. This is useful for debugging and testing, or for having full control over the state transitions in your application.

```ts
const store = createStore({
  context: { count: 0 },
  emits: {
    incremented: (payload: { by: number }) => {},
  },
  on: {
    inc: (context, event: { by: number }, enqueue) => {
      enqueue.emit.incremented({ by: event.by });

      enqueue.effect(() => {
        setTimeout(() => {
          store.send({ type: 'increment' });
        }, 1000);
      });

      return {
        ...context,
        count: context.count + event.by,
      };
    },
  },
});

const snapshot = store.getSnapshot();

// [!code highlight:4]
const [nextState, effects] = store.transition(snapshot, {
  type: 'inc',
  by: 1,
});

console.log(nextState.context);
// => { count: 1 }

console.log(effects);
// => [
//   { type: 'incremented', by: 1 },
//   Function
// ]

// The store's state is unchanged
console.log(store.getSnapshot().context);
// => { count: 0 }
```

If you need to determine the next state from the store's initial state, you can get the initial snapshot using `store.getInitialSnapshot()`:

```ts
const initialSnapshot = store.getInitialSnapshot();

const [nextState, effects] = store.transition(initialSnapshot, {
  type: 'inc',
  by: 1,
});
```

## Selectors

Store selectors provide an efficient way to select and subscribe to specific parts of your store's state. With store selectors, you can:

* Get the current value of a specific part of state via `selector.get()`
* Subscribe to changes of only that specific part via `selector.subscribe(observer)`
* Optimize performance by only notifying subscribers when the selected value actually changes via `selector.subscribe(observer, equalityFn)`

You can create a selector using `store.select(selector)`:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  context: {
    position: { x: 0, y: 0 },
    name: 'John',
    age: 30,
  },
  on: {
    positionUpdated: (
      context,
      event: { position: { x: number; y: number } },
    ) => ({
      ...context,
      position: event.position,
    }),
  },
});

// Create a selector for the position
const position = store.select((context) => context.position);

// Get current value
console.log(position.get()); // { x: 0, y: 0 }

// Subscribe to changes
position.subscribe((position) => {
  console.log('Position updated:', position);
});

// When position updates, only position subscribers are notified
store.trigger.positionUpdated({ position: { x: 100, y: 200 } });
// Logs: Position updated: { x: 100, y: 200 }
```

### Custom Equality Functions

You can provide a custom equality function as the second argument to `store.select(selector, equalityFn)` to control when subscribers should be notified:

```ts
const position = store.select(
  (state) => state.context.position,
  // Only notify if x coordinate changes
  (prev, next) => prev.x === next.x,
);
```

XState Store also provides a `shallowEqual` function that can be used as a default equality function:

```ts
import { shallowEqual } from '@xstate/store';

const position = store.select((state) => state.context.position, shallowEqual);
```

## Atoms

An atom is a lightweight, reactive piece of state that can be read, written to, and subscribed to. Atoms can be used standalone or combined with other atoms and stores for more complex state management.

You can:

* Create an atom with `createAtom(initialValue)`
* Read the atom's value with `atom.get()`
* Subscribe to changes with `atom.subscribe(observer)`
* Update the atom with `atom.set(value)`

<Callout>
  Atoms are best used for:

  * Simple, independent pieces of state
  * Derived/computed values
  * Bridging between stores and external state
  * When you need direct value updates without constraints

  For state that needs to follow specific transition rules or complex update logic, consider using a store instead.
</Callout>

### Creating Atoms

Create an atom using `createAtom()` with an initial value:

```ts
import { createAtom } from '@xstate/store';

// Create an atom with a primitive value
const countAtom = createAtom(0);

// Create an atom with an object
const userAtom = createAtom({ name: 'David', count: 100 });
```

### Reading and Writing Atoms

You can read an atom's value using `atom.get()` and update it using `atom.set()`:

```ts
const countAtom = createAtom(0);

// Read the current value
console.log(countAtom.get()); // 0

// Set a new value directly
countAtom.set(1); // 1

// Update value using a function
countAtom.set((prev) => prev + 1); // 2

const count = createAtom(0);
count.get(); // 0
count.set(1); // 1
count.set((prev) => prev + 1); // 2

// Recomputes when count changes
const laugh = createAtom(() => {
  return 'ha'.repeat(count.get());
});
laugh.subscribe((value) => {
  console.log(value);
});
```

### Subscribing to Changes

Atoms support subscriptions to react to value changes:

```ts
const countAtom = createAtom(0);

// Subscribe to changes
const subscription = countAtom.subscribe((newValue) => {
  console.log('Count changed:', newValue);
});

countAtom.set(1); // Logs: "Count changed: 1"

// Unsubscribe when done
subscription.unsubscribe();

countAtom.set(2); // Does not log anything
```

### Combined Atoms

You can create derived atoms that combine values from other atoms, stores, or selectors:

```ts
const nameAtom = createAtom('David');
const ageAtom = createAtom(30);

// Combine multiple atoms
const userAtom = createAtom(() => ({
  name: nameAtom.get(),
  age: ageAtom.get(),
}));

// Combined atoms are read-only and update automatically
console.log(userAtom.get()); // { name: 'David', age: 30 }
nameAtom.set('John');
console.log(userAtom.get()); // { name: 'John', age: 30 }
ageAtom.set(31);
console.log(userAtom.get()); // { name: 'John', age: 31 }
```

#### Accessing Previous Value in Computed Atoms

*Since v3.12.0*

Computed atoms can access their previous computed value through the second parameter. This is useful for any derived state that depends on its own previous value:

```ts
const countAtom = createAtom(0);

// Running total that aggregates all count changes
// Note: Specify the type parameter for proper inference of prev
const totalAtom = createAtom<number>((_, prev) => countAtom.get() + (prev ?? 0));

console.log(totalAtom.get()); // 0
countAtom.set(5);
console.log(totalAtom.get()); // 5 (0 + 5)
countAtom.set(3);
console.log(totalAtom.get()); // 8 (5 + 3)
countAtom.set(2);
console.log(totalAtom.get()); // 10 (8 + 2)
```

The previous value is `undefined` on the first computation. Since TypeScript cannot infer the type of `prev`, you should provide a type parameter to `createAtom<T>()` for type safety.

<Callout type="warning">
  The `read(...)` function for combining atoms is deprecated since v3.5.0. Instead, use the direct `.get()` method on atoms when creating combined atoms:

  ```ts
  // ❌ Deprecated
  const combinedAtom = createAtom((read) => read(atomA) + read(atomB));

  // ✅ New API
  const combinedAtom = createAtom(() => atomA.get() + atomB.get());
  ```
</Callout>

### Async Atoms

*Since v3.6.0*

Async atoms are a special type of atom that handle asynchronous values. They are created using `createAsyncAtom(…)` and take an async function that returns a promise. The atom's value represents the loading state of the async operation.

The value of an async atom will be an object with a `status` property that can be:

* `'pending'` - while the promise is resolving
* `'done'` with a `data` property containing the resolved value
* `'error'` with an `error` property containing the error that was thrown

```ts
import { createAsyncAtom } from '@xstate/store';

const userAtom = createAsyncAtom(async () => {
  const response = await fetch('/api/user');
  return response.json();
});

userAtom.subscribe((snapshot) => {
  if (snapshot.status === 'pending') {
    console.log(snapshot);
    // { status: 'pending' }
  } else if (snapshot.status === 'done') {
    console.log(snapshot);
    // { status: 'done', data: { name: 'David', ... } }
  } else if (snapshot.status === 'error') {
    console.log(snapshot);
    // { status: 'error', error: Error('Failed to fetch') }
  }
});
```

### Working with Stores and Selectors

Atoms can seamlessly integrate with XState stores and selectors:

```ts
const store = createStore({
  context: { count: 0 },
  on: {
    increment: (context) => ({ ...context, count: context.count + 1 }),
  },
});

// Create an atom from a store selector
const countSelector = store.select((state) => state.context.count);
const doubleCountAtom = createAtom(() => 2 * countSelector.get());

console.log(doubleCountAtom.get()); // 0
store.trigger.increment();
console.log(doubleCountAtom.get()); // 2
```

<Callout>
  Combined atoms are read-only by design. If you need to update multiple values atomically, consider using a store instead.
</Callout>

### Using Atoms with React

*Since v3.7.0*

The `useAtom` hook is the simplest way to use atoms in React. You can use it with or without a selector. The `useAtom` hook only returns the (selected) value of the atom, since you can set the value of the atom directly using `atom.set(…)`.

```tsx
import { createAtom } from '@xstate/store';
import { useAtom } from '@xstate/store/react';

const countAtom = createAtom(0);

function Counter() {
  // Get the full atom value
  const count = useAtom(countAtom);

  return (
    <div>
      <button onClick={() => countAtom.set((prevCount) => prevCount + 1)}>
        Increment
      </button>
      <button onClick={() => countAtom.set(0)}>Reset</button>
      <div>Count: {count}</div>
    </div>
  );
}

// With a selector
const userAtom = createAtom({ name: 'test', age: 25 });

function UserName() {
  // Get just the name
  const name = useAtom(userAtom, (state) => state.name);

  return <div>Name: {name}</div>;
}
```

You can also provide a custom comparison function to control re-renders:

```tsx
const userAtom = createAtom({ name: 'David' /* ... */ });

function UserProfile() {
  const name = useAtom(
    userAtom,
    (state) => state.name,
    // Custom compare function - case-insensitive comparison
    (a, b) => a.toLowerCase() === b.toLowerCase(),
  );

  return (
    <>
      <div>Name: {name}</div>
      <input
        type="text"
        value={name}
        onChange={(e) =>
          userAtom.set((prev) => ({ ...prev, name: e.target.value }))
        }
      />
    </>
  );
}
```

<details>
  <summary>
    Using `useSelector`
  </summary>

  Alternatively, you can use the `useSelector` hook, which is similar to `useAtom` with a selector:

  ```tsx
  import { createAtom } from '@xstate/store';
  import { useSelector } from '@xstate/store/react';

  const countAtom = createAtom(0);

  function Counter() {
    const count = useSelector(countAtom, (s) => s);

    return (
      <div>
        <button onClick={() => countAtom.set((prev) => prev + 1)}>
          Increment
        </button>
        <div>Count: {count}</div>
      </div>
    );
  }
  ```
</details>

## Inspection

Just like with XState, you can use the [Inspect API](./inspection) to inspect events sent to the store and state transitions within the store by using the .inspect method:

```ts
import { createStore } from '@xstate/store';

const store = createStore({
  // ...
});

// [!code highlight:5]
store.inspect((inspectionEvent) => {
  // type: '@xstate.snapshot' or
  // type: '@xstate.event'
  console.log(inspectionEvent);
});
```

<Callout>
  Since the store is automatically started, inspectors will immediately receive the initial state snapshot.
</Callout>

The `.inspect(…)` method returns a subscription object:

```ts
import { createStore } from '@xstate/store';

const sub = store.inspect((inspectionEvent) => {
  console.log(inspectionEvent);
});

// Stop listening for inspection events
sub.unsubscribe();
```

You can use the [Stately Inspector](./inspector) to inspect and visualize the state of the store.

```ts
import { createBrowserInspector } from '@statelyai/inspect';
import { createStore } from '@xstate/store';

const store = createStore({
  // ...
});

const inspector = createBrowserInspector({
  // ...
});

store.inspect(inspector);
```

## Using Immer

You can use the `produce(…)` function from [Immer](https://immerjs.github.io/immer/) to update the `context` in transitions:

```ts
import { createStore } from '@xstate/store';
import { produce } from 'immer';

const store = createStore({
  context: { count: 0, todos: [] },
  on: {
    inc: (context, event: { by: number }) =>
      produce(context, (draft) => {
        draft.count += event.by;
      }),
    addTodo: (context, event: { todo: string }) =>
      produce(context, (draft) => {
        draft.todos.push(event.todo);
      }),
    // Not using a producer
    resetCount: (context) => ({
      ...context,
      count: 0,
    }),
  },
});
```

<details>
  <summary>
    Deprecated: 

    <code>createStoreWithProducer(…)</code>
  </summary>

  <Callout type="warning">
    This API is deprecated. Use `produce` from [Immer](https://immerjs.github.io/immer/) or similar libraries directly with `createStore(…)` instead.
  </Callout>

  In previous versions of `@xstate/store`, you could use the `createStoreWithProducer(…)` function to pass in a producer function to update the `context` for every transition. This will not be supported in future versions of `@xstate/store`. Instead, you can use the `produce(…)` function from [Immer](https://immerjs.github.io/immer/) or similar libraries directly with `createStore(…)`.

  ```ts
  import { createStore } from '@xstate/store';
  import { produce } from 'immer';

  // Deprecated API
  const store = createStoreWithProducer(produce, {
    context: { count: 0, todos: [] },
    on: {
      inc: (context, event: { by: number }) => {
        // No return; handled by Immer
        context.count += event.by;
      },
      addTodo: (context, event: { todo: string }) => {
        // No return; handled by Immer
        context.todos.push(event.todo);
      },
    },
  });

  // ...
  ```
</details>

## Usage with React

Use the `useSelector(store, selector)` hook from [`@xstate/store-react`](/docs/xstate-store/react) to subscribe to the store and get the current state.

```tsx
import { createStore, useSelector } from '@xstate/store-react';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

function Counter() {
  const count = useSelector(store, (state) => state.context.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.trigger.inc()}>+1</button>
      <button onClick={() => store.trigger.inc({ by: 5 })}>+5</button>
    </div>
  );
}
```

For the full React API including `useStore`, `useAtom`, and `createStoreHook`, see the [React documentation](/docs/xstate-store/react).

## Usage with other frameworks

* **Vue**: See [`@xstate/store-vue`](/docs/xstate-store/vue)
* **Svelte**: See [`@xstate/store-svelte`](/docs/xstate-store/svelte)
* **Solid**: See [`@xstate/store-solid`](/docs/xstate-store/solid)
* **Angular**: See [`@xstate/store-angular`](/docs/xstate-store/angular)
* **Preact**: See [`@xstate/store-preact`](/docs/xstate-store/preact)

## Undo/Redo

*Since v3.14.0*

XState Store provides built-in undo/redo functionality through the `undoRedo` store extension. You can choose between two strategies:

* **Event-sourced** (default): Stores events in history and replays them during undo/redo. Memory efficient and provides precise control over transaction grouping and event skipping while properly replaying side effects.
* **Snapshot**: Stores full state snapshots in history. Useful when you need to preserve state that might be lost during event replay, or when you want to limit history size.

### Basic Usage

Import `undoRedo` from `@xstate/store/undo` and use it with the `.with()` method. The store will have `undo()` and `redo()` methods available on `store.trigger`:

```ts
import { createStore } from '@xstate/store';
import { undoRedo } from '@xstate/store/undo';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context) => ({ count: context.count + 1 }),
  },
}).with(undoRedo());
```

You can undo and redo multiple events in sequence. Each `undo()` call moves back one event in history, and each `redo()` call moves forward one event.

<details>
  <summary>
    Example: Basic undo/redo
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo());

  // Use the store normally
  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2

  // Undo the last event
  store.trigger.undo(); // count = 1

  // Redo the undone event
  store.trigger.redo(); // count = 2
  ```
</details>

<details>
  <summary>
    Example: Multiple undo/redo operations
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo());

  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2
  store.trigger.inc(); // count = 3

  store.trigger.undo(); // count = 2
  store.trigger.undo(); // count = 1
  store.trigger.redo(); // count = 2
  store.trigger.redo(); // count = 3
  ```
</details>

### Transactions

You can group multiple events into transactions so they are undone/redone together using `getTransactionId`. Events with the same transaction ID are grouped together and undone/redone as a single unit:

```ts
const store = createStore({
  // ...
}).with(undoRedo({
  getTransactionId: (event, snapshot) => {
    // Return a transaction ID to group events
    // Events with the same ID are grouped together
  },
}));
```

<details>
  <summary>
    Example: Group events by type
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      dec: (context) => ({ count: context.count - 1 }),
    },
  }).with(undoRedo({
    // Group events by their type
    getTransactionId: (event) => event.type,
  }));

  // First transaction: increments
  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2

  // Second transaction: decrements
  store.trigger.dec(); // count = 1
  store.trigger.dec(); // count = 0

  // Undo second transaction (both decrements)
  store.trigger.undo(); // count = 2

  // Undo first transaction (both increments)
  store.trigger.undo(); // count = 0
  ```
</details>

<details>
  <summary>
    Example: Group events by context state
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0, transactionId: null as string | null },
    on: {
      inc: (context) => ({ ...context, count: context.count + 1 }),
      setTransactionId: (context, event: { id: string }) => ({
        ...context,
        transactionId: event.id,
      }),
    },
  }).with(undoRedo({
    // Use context state to determine transaction grouping
    getTransactionId: (event, snapshot) => snapshot.context.transactionId,
  }));

  store.trigger.inc(); // count = 1
  store.trigger.setTransactionId({ id: 'batch-1' });
  store.trigger.inc(); // count = 2
  store.trigger.inc(); // count = 3
  store.trigger.inc(); // count = 4

  store.trigger.setTransactionId({ id: 'batch-2' });
  store.trigger.inc(); // count = 5
  store.trigger.inc(); // count = 6

  // Undo second transaction (all events with batch-2)
  store.trigger.undo(); // count = 4

  // Undo first transaction (all events with batch-1)
  store.trigger.undo(); // count = 1
  ```
</details>

### Skipping Events

You can configure certain events to be skipped from the undo/redo history using `skipEvent`. Skipped events are not stored in history and are not replayed during undo/redo operations:

```ts
const store = createStore({
  // ...
}).with(undoRedo({
  skipEvent: (event, snapshot) => {
    // Return true to skip this event from history
    // Return false to include it in history
  },
}));
```

<details>
  <summary>
    Example: Skip specific event types
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      log: (context) => context, // No state change, just logging
    },
  }).with(undoRedo({
    // Skip log events from undo/redo history
    skipEvent: (event) => event.type === 'log',
  }));

  store.trigger.inc(); // count = 1
  store.trigger.log(); // count = 1 (logged but not undoable)
  store.trigger.inc(); // count = 2

  // Undo skips log event, only undoes inc events
  store.trigger.undo(); // count = 1
  ```
</details>

<details>
  <summary>
    Example: Skip events based on state
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo({
    // Skip events when count is 3 or more
    skipEvent: (event, snapshot) => snapshot.context.count >= 3,
  }));

  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2
  store.trigger.inc(); // count = 3
  store.trigger.inc(); // count = 4 (skipped from history)

  store.trigger.undo(); // count = 2 (skips the last inc)
  ```
</details>

### Effects and Emitted Events

Emitted events are properly replayed during undo/redo operations. When you undo or redo an event, any events that were emitted during the original transition are re-emitted:

```ts
const store = createStore({
  // ...
  emits: {
    // ...
  },
  on: {
    // ...
  },
}).with(undoRedo());
```

Skipped events do not have their emitted events replayed during undo/redo operations.

<details>
  <summary>
    Example: Emitted events are replayed
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    emits: {
      changed: (payload: { value: number }) => {},
    },
    on: {
      inc: (context, event, enqueue) => {
        enqueue.emit.changed({ value: context.count + 1 });
        return { count: context.count + 1 };
      },
    },
  }).with(undoRedo());

  const emittedEvents: Array<{ type: string; value: number }> = [];
  store.on('changed', (event) => {
    emittedEvents.push(event);
  });

  store.trigger.inc(); // emits changed(1)
  store.trigger.undo(); // emits changed(0)
  store.trigger.redo(); // emits changed(1)

  // emittedEvents = [
  //   { type: 'changed', value: 1 },
  //   { type: 'changed', value: 0 },
  //   { type: 'changed', value: 1 }
  // ]
  ```
</details>

<details>
  <summary>
    Example: Skipped events don't replay emitted events
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    emits: {
      changed: (payload: { value: number }) => {},
      logged: (payload: { message: string }) => {},
    },
    on: {
      inc: (context, event, enqueue) => {
        enqueue.emit.changed({ value: context.count + 1 });
        return { count: context.count + 1 };
      },
      log: (context, event: { message: string }, enqueue) => {
        enqueue.emit.logged({ message: event.message });
        return context; // No state change
      },
    },
  }).with(undoRedo({
    skipEvent: (event) => event.type === 'log',
  }));

  const emittedEvents: any[] = [];
  store.on('changed', (event) => emittedEvents.push(event));
  store.on('logged', (event) => emittedEvents.push(event));

  store.trigger.inc(); // emits changed(1)
  store.trigger.log({ message: 'test' }); // emits logged('test') but not stored in history
  store.trigger.inc(); // emits changed(2)

  emittedEvents.length = 0;
  store.trigger.undo(); // emits changed(1)
  store.trigger.undo(); // emits changed(0)
  store.trigger.redo(); // emits changed(1)
  store.trigger.redo(); // emits changed(2)

  // Only inc events are emitted during undo/redo, log events are skipped
  // emittedEvents = [
  //   { type: 'changed', value: 1 },
  //   { type: 'changed', value: 0 },
  //   { type: 'changed', value: 1 },
  //   { type: 'changed', value: 2 }
  // ]
  ```
</details>

### Interleaving Undo/Redo with New Events

When you send a new event after undoing, the redo stack is cleared. This ensures that the history remains consistent and prevents unexpected behavior when mixing undo/redo with new events.

<details>
  <summary>
    Example: New events clear redo stack
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      dec: (context) => ({ count: context.count - 1 }),
    },
  }).with(undoRedo());

  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2
  store.trigger.undo(); // count = 1
  store.trigger.dec(); // count = 0 (clears redo stack)

  // Redo should not work as we added a new event after undo
  store.trigger.redo(); // count = 0 (no change)
  ```
</details>

### Empty Operations

Undo/redo operations do nothing when there's no history to undo/redo. Calling `undo()` when there's no history, or `redo()` when there's nothing to redo, will have no effect on the store state.

<details>
  <summary>
    Example: Empty operations
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo());

  const initialSnapshot = store.getSnapshot();

  // Undo with empty history does nothing
  store.trigger.undo();
  expect(store.getSnapshot()).toEqual(initialSnapshot);

  // Redo with empty redo stack does nothing
  store.trigger.redo();
  expect(store.getSnapshot()).toEqual(initialSnapshot);
  ```
</details>

### Snapshot Strategy

By default, `undoRedo` uses the event-sourced strategy. You can switch to the snapshot strategy by setting `strategy: 'snapshot'`. The snapshot strategy stores full state snapshots in history, which can be useful when:

* You need to preserve state that might be lost during event replay
* You want to limit history size with `historyLimit`
* You want to skip duplicate snapshots with a `compare` function

```ts
const store = createStore({
  // ...
}).with(undoRedo({ strategy: 'snapshot' }));
```

<details>
  <summary>
    Example: Using snapshot strategy
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo({ strategy: 'snapshot' }));

  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2
  store.trigger.undo(); // count = 1
  store.trigger.redo(); // count = 2
  ```
</details>

#### History Limit

With the snapshot strategy, you can limit the number of snapshots stored in history using `historyLimit`. When the limit is reached, older snapshots are removed from history:

```ts
const store = createStore({
  // ...
}).with(undoRedo({
  strategy: 'snapshot',
  historyLimit: 2,
}));
```

<details>
  <summary>
    Example: History limit
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
    },
  }).with(undoRedo({
    strategy: 'snapshot',
    historyLimit: 2,
  }));

  store.trigger.inc(); // count = 1
  store.trigger.inc(); // count = 2
  store.trigger.inc(); // count = 3
  store.trigger.inc(); // count = 4

  // Can only undo 2 times because of history limit
  store.trigger.undo(); // count = 3
  store.trigger.undo(); // count = 2
  store.trigger.undo(); // count = 2 (limit reached, no change)
  ```
</details>

#### Compare Function

With the snapshot strategy, you can provide a `compare` function to skip duplicate snapshots. If the compare function returns `true` for two consecutive snapshots, the second snapshot is not saved in history:

```ts
const store = createStore({
  // ...
}).with(undoRedo({
  strategy: 'snapshot',
  compare: (past, current) => {
    // Return true to skip saving this snapshot (duplicate)
    // Return false to save this snapshot
  },
}));
```

<details>
  <summary>
    Example: Skip duplicate snapshots
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      noop: (context) => context, // No state change
    },
  }).with(undoRedo({
    strategy: 'snapshot',
    // Skip snapshots where count hasn't changed
    compare: (past, current) => past.context.count === current.context.count,
  }));

  store.trigger.inc(); // count = 1 (saved)
  store.trigger.noop(); // count = 1 (duplicate, not saved)
  store.trigger.noop(); // count = 1 (duplicate, not saved)
  store.trigger.inc(); // count = 2 (saved)

  // Should only have 2 snapshots in history (0 and 1), not 4
  store.trigger.undo(); // count = 1
  store.trigger.undo(); // count = 0
  store.trigger.undo(); // count = 0 (no change)
  ```
</details>

<details>
  <summary>
    Example: All snapshots saved without compare function
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      noop: (context) => context,
    },
  }).with(undoRedo({ strategy: 'snapshot' }));

  store.trigger.inc(); // count = 1 (saved)
  store.trigger.noop(); // count = 1 (saved even though duplicate)
  store.trigger.noop(); // count = 1 (saved even though duplicate)
  store.trigger.inc(); // count = 2 (saved)

  // Should have 4 snapshots in history (0, 1, 1, 1)
  store.trigger.undo(); // count = 1
  store.trigger.undo(); // count = 1
  store.trigger.undo(); // count = 1
  store.trigger.undo(); // count = 0
  ```
</details>

#### Preserving State with Skipped Events

With the snapshot strategy, skipped events preserve their state in snapshots. This is because snapshots capture the full state at a point in time, including changes made by skipped events:

```ts
const store = createStore({
  // ...
}).with(undoRedo({
  strategy: 'snapshot',
  skipEvent: (event) => {
    // ...
  },
}));
```

<details>
  <summary>
    Example: Preserving state with skipped events
  </summary>

  ```ts
  const store = createStore({
    context: { count: 0, logs: [] as string[] },
    on: {
      inc: (context) => ({ count: context.count + 1, logs: context.logs }),
      log: (context, event: { type: 'log'; message: string }) => ({
        logs: [...context.logs, event.message],
        count: context.count,
      }),
    },
  }).with(undoRedo({
    strategy: 'snapshot',
    skipEvent: (event) => event.type === 'log',
  }));

  store.trigger.inc(); // count = 1, logs = []
  store.trigger.log({ message: 'first log' }); // count = 1, logs = ['first log'] (not tracked)
  store.trigger.inc(); // count = 2, logs = ['first log']

  // Undo should restore snapshot before second inc, which includes the log
  store.trigger.undo(); // count = 1, logs = ['first log']
  ```
</details>

### Configuration Options

The `undoRedo` function accepts a configuration object with these options:

* `strategy`: `'event-sourced'` (default) or `'snapshot'` - The strategy to use for history
* `getTransactionId(event, snapshot)`: Function to determine transaction grouping
* `skipEvent(event, snapshot)`: Function to determine if an event should be skipped
* `historyLimit` (snapshot strategy only): Maximum number of snapshots to store in history
* `compare` (snapshot strategy only): Function to compare snapshots and skip duplicates

### Type Safety

The undo/redo extension preserves all TypeScript types from your original store:

```ts
const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context) => ({ count: context.count + 1 }),
  },
}).with(undoRedo());

// Type safety is preserved
store.getSnapshot().context satisfies { count: number };

// @ts-expect-error
store.getSnapshot().context.foo;

// @ts-expect-error
store.trigger.dec();
```

### Notes

* **Event-sourced by default**: The default strategy stores events, not snapshots, making it memory efficient
* **New events clear redo stack**: When you send a new event after undoing, the redo stack is cleared
* **Empty operations**: Undo/redo operations do nothing when there's no history to undo/redo
* **Type safety**: The undo/redo extension preserves all TypeScript types from your original store
* **Emitted events**: Events emitted during transitions are replayed during undo/redo operations

## Using XState Store with XState

You may notice that stores are very similar to [actors in XState](./actors). This is very much by design. XState's actors are very powerful, but may also be too complex for simple use cases, which is why `@xstate/store` exists.

However, if you have existing XState code, and you enjoy the simplicity of creating store logic with `@xstate/store`, you can use the `fromStore(context, transitions)` actor logic creator to create XState-compatible store logic that can be passed to the `createActor(storeLogic)` function:

```ts
import { fromStore } from '@xstate/store';
import { createActor } from 'xstate';

// Instead of:
// const store = createStore( ... };
const storeLogic = fromStore({
  context: { count: 0, incremented: false /* ... */ },
  on: {
    // [!code highlight:5]
    inc: {
      count: (context, event) => context.count + 1,
      // Static values do not need to be wrapped in a function
      incremented: true,
    },
  },
});

const store = createActor(storeLogic);
store.subscribe((snapshot) => {
  console.log(snapshot);
});
store.start();

store.send({
  type: 'inc',
});
```

In short, you can convert `createStore(…)` to `fromStore(…)` just by changing one line of code. Note that `fromStore(…)` returns *store logic*, and not a store actor instance. Store logic is passed to `createActor(storeLogic)` to create a store actor instance:

```ts
import { fromStore } from '@xstate/store';

// Instead of:
// const store = createStore({
const storeLogic = fromStore({
  context: {
    // ...
  },
  on: {
    // ...
  },
});

// Create the store (actor)
const storeActor = createActor(storeLogic);
```

Using `fromStore(…)` to create store actor logic also has the advantage of allowing you to provide `input` by using a context function that takes in the `input` and *returns* the initial `context`:

```ts
import { fromStore } from '@xstate/store';

const storeLogic = fromStore({
  // [!code highlight:3]
  context: (initialCount: number) => ({
    count: initialCount,
  }),
  on: {
    // ...
  },
});

const actor = createActor(storeLogic, {
  // [!code highlight:1]
  input: 42,
});
```

## Converting stores to state machines

If you have a store that you want to convert to a state machine in XState, you can convert it in a straightforward way:

1. Use `createMachine(…)` (imported from `xstate`) instead of `createStore(…)` (imported from `@xstate/store`) to create a state machine.
2. Wrap the assignments in an `assign(…)` action creator (imported from `xstate`) and move that to the `actions` property of the transition.
3. Destructure `context` and `event` from the first argument instead of them being separate arguments.

For example, here is our store before conversion:

```ts
import { createMachine } from 'xstate';

// 1. Use `createMachine(…)` instead of `createStore(…)`
const store = createStore({
  context: { count: 0, name: 'David' },
  on: {
    inc: {
      // 2. Wrap the assignments in `assign(…)`
      count: (context, event: { by: number }) => context.count + event.by,
    },
  },
});

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

And here is the store as a state machine after conversion:

```ts
import { createMachine } from 'xstate';

// const store = createStore({
//   context: { count: 0, name: 'David' },
//   on: {
//     inc: {
//       count: (context, event: { by: number }) => context.count + event.by
//     }
//   }
// });

// 1. Use `createMachine(…)` instead of `createStore(…)`
const machine = createMachine({
  context: {
    count: 0,
    name: 'David',
  },
  on: {
    inc: {
      // 2. Wrap the assignments in `assign(…)`
      actions: assign({
        // 3. Destructure `context` and `event` from the first argument
        count: ({ context, event }) => context.count + event.by,
      }),
    },
  },
});
```

For stronger typing, use the [`setup(…)` function](./setup) to strongly type the `context` and `events`:

```ts
import { setup } from 'xstate';

const machine = setup({
  // [!code highlight:4]
  types: {
    context: {} as { count: number; name: string },
    events: {} as { type: 'inc'; by: number },
  },
}).createMachine({
  // Same as the previous example
});
```

## Comparison

This section compares XState Store to other popular state management libraries in TypeScript. It is meant for reference purposes only, and not intended to favor one approach over the other. The examples are copied from [Zustand's comparison docs](https://docs.pmnd.rs/zustand/getting-started/comparison).

### Compare to Zustand

**Zustand**

```ts
import { create } from 'zustand';

type State = {
  count: number;
};

type Actions = {
  increment: (qty: number) => void;
  decrement: (qty: number) => void;
};

const useCountStore = create<State & Actions>((set) => ({
  count: 0,
  increment: (qty: number) =>
    set((state) => ({
      count: state.count + qty,
    })),
  decrement: (qty: number) =>
    set((state) => ({
      count: state.count - qty,
    })),
}));

const Component = () => {
  const count = useCountStore((state) => state.count);
  const increment = useCountStore((state) => state.increment);
  const decrement = useCountStore((state) => state.decrement);
  // ...
};
```

**XState Store**

```ts
import { createStore } from '@xstate/store';
import { useSelector } from '@xstate/store/react';

const store = createStore({
  context: {
    count: 0,
  },
  on: {
    increment: (context, { qty }: { qty: number }) => ({
      ...context,
      count: context.count + qty,
    }),
    decrement: (context, { qty }: { qty: number }) => ({
      ...context,
      count: context.count - qty,
    }),
  },
});

const Component = () => {
  const count = useSelector(store, (state) => state.context.count);
  const { increment, decrement } = store.trigger;
  // ...
};
```


# @xstate/svelte (/docs/xstate-svelte)

The [@xstate/svelte package](https://github.com/statelyai/xstate/tree/main/packages/xstate-svelte) contains utilities for using [XState](https://github.com/statelyai/xstate) with [Svelte](https://github.com/sveltejs/svelte).

## Templates

Use the following templates to get started quickly with XState and Svelte:

* [XState Svelte template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/svelte-ts)
* [XState Svelte template (StackBlitz)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/svelte-ts?file=%2Fsrc%2FfeedbackMachine.ts)

## Installation

Install the latest versions of both `xstate` and `@xstate/svelte`. `xstate` is a peer dependency of `@xstate/svelte`.

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate @xstate/svelte
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate @xstate/svelte
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate @xstate/svelte
    ```
  </Tab>
</Tabs>

## API

### `useMachine(machine, options?)`

A function that creates an actor from the given `machine` and starts an actor that runs for the lifetime of the component.

**Arguments**

* `machine` - An [XState machine](machines).
* `options` (optional) - Actor options

**Returns** `{ snapshot, send, actorRef}`:

* `snapshot` - A [Svelte store](https://svelte.dev/docs#svelte_store) representing the current state of the machine
* `send` - A function that sends events to the running actor ref.
* `actorRef` - The created actor ref.

### `useSelector(actorRef, selector, compare?, getSnapshot?)`

A function that returns [Svelte store](https://svelte.dev/docs#svelte_store) representing the selected value from the snapshot of an `actorRef`, such as an actor. The store will only be updated when the selected value changes, as determined by the optional `compare` function.

**Arguments**

* `actorRef` - An actor ref
* `selector` - a function that takes in an actor’s current state (`snapshot`) as an argument and returns the desired selected value.
* `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.

#### Examples

*Coming soon*

## Matching States

When using [hierarchical](https://xstate.js.org/docs/guides/hierarchical.html) and [parallel](https://xstate.js.org/docs/guides/parallel.html) machines, the state values will be objects, not strings. In this case, it is best to use [`state.matches(...)`](https://xstate.js.org/docs/guides/states.html#state-methods-and-properties).

```svelte
{#if $state.matches('idle')}
  //
{:else if $state.matches({ loading: 'user' })}
  //
{:else if $state.matches({ loading: 'friends' })}
  //
{/if}
```

## Persisted and Rehydrated State

You can persist and rehydrate state with `useMachine(...)` via `options.snapshot`:

```js
// Get the persisted state config object from somewhere, e.g. localStorage
// [!code highlight:3]
const persistedState = JSON.parse(
  localStorage.getItem('some-persisted-state-key'),
);

const { snapshot, send } = useMachine(someMachine, {
  // [!code highlight:1]
  snapshot: persistedState,
});

// state will initially be that persisted state, not the machine’s initialState
```


# @xstate/test (/docs/xstate-test)

<Callout>
  The latest version of the model-based testing utilities (prev. `@xstate/test`) are now part of the latest `@xstate/graph` package.

  Documentation for `@xstate/graph` (including the testing utilities) is coming soon; the documentation below is for `@xstate/test@beta`.
</Callout>

The [@xstate/test package](https://github.com/statelyai/xstate/tree/main/packages/xstate-test) contains utilities for facilitating [model-based testing](https://en.wikipedia.org/wiki/Model-based_testing) for any software.

**Watch the talk**: [Write Fewer Tests! From Automation to Autogeneration](https://slides.com/davidkhourshid/mbt) at React Rally 2019 ([🎥 Video](https://www.youtube.com/watch?v=tpNmPKjPSFQ))

## Quick start

1. Install `xstate` and `@xstate/test`:

```bash
npm install xstate @xstate/test
```

2. Create the machine that will be used to model the system under test (SUT):

```js
import { createMachine } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {
      on: {
        TOGGLE: 'active',
      },
    },
    active: {
      on: {
        TOGGLE: 'inactive',
      },
    },
  },
});
```

3. Add assertions for each state in the machine (in this example, using [Puppeteer](https://github.com/GoogleChrome/puppeteer)):

```js
// ...

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {
      on: {
        /* ... */
      },
      meta: {
        test: async (page) => {
          await page.waitFor('input:checked');
        },
      },
    },
    active: {
      on: {
        /* ... */
      },
      meta: {
        test: async (page) => {
          await page.waitFor('input:not(:checked)');
        },
      },
    },
  },
});
```

4. Create the model:

```js
import { createMachine } from 'xstate';
import { createModel } from '@xstate/test';

const toggleMachine = createMachine(/* ... */);

const toggleModel = createModel(toggleMachine).withEvents({
  TOGGLE: {
    exec: async (page) => {
      await page.click('input');
    },
  },
});
```

5. Create test plans and run the tests with coverage:

```js
// ...

describe('toggle', () => {
  const testPlans = toggleModel.getShortestPathPlans();

  testPlans.forEach((plan) => {
    describe(plan.description, () => {
      plan.paths.forEach((path) => {
        it(path.description, async () => {
          // do any setup, then...

          await path.test(page);
        });
      });
    });
  });

  it('should have full coverage', () => {
    return toggleModel.testCoverage();
  });
});
```

## API

### `createModel(machine, options?)`

Creates an abstract testing model based on the `machine` passed in.

| Argument   | Type             | Description                                    |
| ---------- | ---------------- | ---------------------------------------------- |
| `machine`  | StateMachine     | The machine used to create the abstract model. |
| `options?` | TestModelOptions | Options to customize the abstract model        |

#### Returns

A `TestModel` instance.

### Methods

#### `model.withEvents(eventsMap)`

Provides testing details for each event. Each key in `eventsMap` is an object whose keys are event types and properties describe the execution and test cases for each event:

* `exec` (function): Function that executes the events. It is given two arguments:
  * `testContext` (any): any contextual testing data
  * `event` (EventObject): the event sent by the testing model
* `cases?` (EventObject\[]): the sample event objects for this event type that can be sent by the testing model.

Example:

```js
const toggleModel = createModel(toggleMachine).withEvents({
  TOGGLE: {
    exec: async (page) => {
      await page.click('input');
    },
  },
});
```

### `testModel.getShortestPathPlans(options?)`

Returns an array of testing plans based on the shortest paths from the test model’s initial state to every other reachable state.

#### Options

| Argument | Type     | Description                                                                                                    |
| -------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `filter` | function | Takes in the `state` and returns `true` if the state should be traversed, or `false` if traversal should stop. |

This is useful for preventing infinite traversals and stack overflow errors:

```js
const todosModel = createModel(todosMachine).withEvents({
  /* ... */
});

const plans = todosModel.getShortestPathPlans({
  // Tell the algorithm to limit state/event adjacency map to states
  // that have less than 5 todos
  filter: (state) => state.context.todos.length < 5,
});
```

### `testModel.getSimplePathPlans(options?)`

Returns an array of testing plans based on the simple paths from the test model’s initial state to every other reachable state.

#### Options

| Argument | Type     | Description                                                                                                    |
| -------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `filter` | function | Takes in the `state` and returns `true` if the state should be traversed, or `false` if traversal should stop. |

### `testModel.getPlanFromEvents(events, options)`

| Argument  | Type                 | Description                                                                         |
| --------- | -------------------- | ----------------------------------------------------------------------------------- |
| `events`  | `EventObject[]`      | The sequence of events to create the plan                                           |
| `options` | `{ target: string }` | An object with a `target` property that should match the target state of the events |

Returns an array with a single testing plan with a single path generated from the `events`.

Throws an error if the last entered state does not match the `options.target`.

### `testModel.testCoverage(options?)`

Tests that all state nodes were covered (traversed) in the exected tests.

#### Options

| Argument | Type     | Description                                                                               |
| -------- | -------- | ----------------------------------------------------------------------------------------- |
| `filter` | function | Takes in each `stateNode` and returns `true` if that state node should have been covered. |

```js
// Only test coverage for state nodes with a `.meta` property defined:

testModel.testCoverage({
  filter: (stateNode) => !!stateNode.meta,
});
```

### `testPlan.description`

The string description of the testing plan, describing the goal of reaching the `testPlan.state`.

### `testPlan.paths`

The testing paths to get from the test model’s initial state to every other reachable state.

### `testPath.description`

The string description of the testing path, describing a sequence of events that will reach the `testPath.state`.

### `testPath.test(testContext)`

Executes each step in `testPath.segments` by:

1. Verifying that the SUT is in `segment.state`
2. Executing the event for `segment.event`

And finally, verifying that the SUT is in the target `testPath.state`.

NOTE: If your model has nested states, the `meta.test` method for each parent state of that nested state is also executed when verifying that the SUT is in that nested state.


# XState VS Code extension (/docs/xstate-vscode-extension)

The [XState VS Code extension](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) enhances the XState development experience by providing VS Code users with autocomplete, typegen, linting, and a visual editor inside VS Code.

<Callout type="warning">
  XState VS Code extension is not fully supported in XState version 5.
</Callout>

<Callout>
  If you don’t use VS Code but use an open source code editor that supports VS Code extensions, you can [download the XState VS Code extension from the OpenVSX registry](https://open-vsx.org/extension/statelyai/stately-vscode).
</Callout>

## Installation

1. Open the command palette with `shift` + `cmd/ctrl` + `p`.
2. Search for the Install Extensions command and hit enter to open the Extensions search.
3. Search for XState to find the XState VS Code extension and install the extension using the Install button.
4. Ensure you have [VS Code setup to insert spaces](https://code.visualstudio.com/docs/editor/codebasics#_indentation); we have noticed problems when tabs are used for indentation.

Once installed, you can run `XState: Open Visual Editor` from the command palette to open any machine at your cursor’s location.

If you have code lens enabled (this can be enabled using `editor.codeLens` setting), ‘Open Visual Editor’ will also float above each `createMachine` call.

You can also [download the VS Code extension from the VS Code marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode) or [download the VS Code extension from the Open VSX marketplace](https://open-vsx.org/extension/statelyai/stately-vscode).

## Features

* **Visually edit machines**. Edit any XState machine with drag-and-drop using the integrated [Stately Visual editor](https://stately.ai/editor).
* **Autocomplete**. Intelligent suggestions for transition targets and initial states.
* **Linting**. Highlights errors and potential bugs in your XState machine definitions.
* **Jump to definition**. Navigate around machines easily with jump to definition on targets, actions, guards, actors and more.

## TypeScript typegen

You can automatically generate intelligent typings for XState with our VS Code extension and [command line interface](developer-tools.mdx#xstate-cli-command-line-interface). [Read more about how to use typegen, and our recommended best practices, on the Typegen page](typegen).

## `xsm` snippet

Use the `xsm` snippet to generate the code required for your state machine quickly. [Watch the ‘XState + VS Code: xsm snippet’ video (1m23s)](https://www.youtube.com/watch?v=Gk3mdlqadYM) for a demo.

## Refactors

When hovering over a named action, guard or actor in a machine, press the lightbulb icon to view available refactors.

## Ignoring machines

If you’d like to ignore linting/autocomplete on a machine, add an `// xstate-ignore-next-line` comment on the line above the machine:

```js
// xstate-ignore-next-line
createMachine({});
```

<Callout title="Machine layout persistence">
  Upon opening an XState machine in VS Code, you may notice a long `@xstate-layout` comment inserted in the code just above the call to `createMachine()`.

  ```js
  const machine =
  /** @xstate-layout N4IgpgJg5mDOIC5QFEA... */
  createMachine({...});
  ```

  This layout string is for persisting manual changes you make to the machine’s layout and is automatically updated by the XState Extension whenever layout changes occur. It is not intended to be human-readable nor manually edited. When updates to this string are made by the extension, the file is not saved until a manual save is performed. The layout algorithm is able to interpret this string and automatically format the machine's layout whenever it is re-opened in Stately Studio’s editor.
</Callout>

<Callout type="warning">
  **Caution**: if you use tab based-indentation in VS Code the extension might not work. [Setup VS Code to insert spaces](https://code.visualstudio.com/docs/editor/codebasics#_indentation) automatically.
</Callout>


# @xstate/vue (/docs/xstate-vue)

The [@xstate/vue package](https://github.com/statelyai/xstate/tree/main/packages/xstate-vue) contains utilities for using [XState](https://github.com/statelyai/xstate) with [Vue](https://github.com/vuejs/vue).

{/* TODO: Vue 2? */}

## Templates

Use the following templates to get started quickly with XState and Vue:

* [XState Vue template (CodeSandbox)](https://codesandbox.io/p/devbox/github/statelyai/xstate/tree/main/templates/vue-ts)
* [XState Vue template (StackBlitz)](https://stackblitz.com/github/statelyai/xstate/tree/main/templates/vue-ts?file=%2Fsrc%2FfeedbackMachine.ts)

## Installation

Install the latest versions of both `xstate` and `@xstate/vue`. `xstate` is a peer dependency of `@xstate/vue`.

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate @xstate/vue
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate @xstate/vue
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate @xstate/vue
    ```
  </Tab>
</Tabs>

{/* TODO: CDN? */}

## API

### `useActor(actorLogic, options?)`

A [Vue composition function](https://v3.vuejs.org/guide/composition-api-introduction.html) that creates an actor from the given `actorLogic` and starts an actor ref that runs for the lifetime of the component.

#### Arguments

* `actorLogic` - An [XState machine](machines)
* `options` (optional) - Actor options

**Returns** `{ snapshot, send, actorRef }`:

* `snapshot` - Represents the current snapshot (state) of the machine as an XState `State` object.
* `send` - A function that sends events to the running actor.
* `actorRef` - The created actor ref.

### `useMachine(machine, options?)`

A [Vue composition function](https://v3.vuejs.org/guide/composition-api-introduction.html) that creates an actor from the given `machine` and starts an actor that runs for the lifetime of the component.

#### Arguments

* `machine` - An [XState machine](machines)
* `options` (optional) - Actor options

**Returns** `{ snapshot, send, actorRef }`:

* `snapshot` - Represents the current snapshot (state) of the machine as an XState `State` object.
* `send` - A function that sends events to the running actor.
* `actorRef` - The created actor ref.

### `useActorRef(actorLogic, options?, observer?)`

A [Vue composition function](https://v3.vuejs.org/guide/composition-api-introduction.html) that returns the `actorRef` created from the `actorLogic` with the actor `options`, if specified. It also sets up a subscription to the `actorRef` with the `observer`, if provided.

#### Arguments

* `actorLogic` - Actor logic
* `options` (optional) - Actor options
* `observer` (optional) - an observer or listener that listens to snapshot updates:
  * an observer (e.g., `{ next: (snapshot) => {/* ... */} }`)
  * or a listener (e.g., `(snapshot) => {/* ... */}`)

#### Examples

```js
import { useActorRef } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';

export default {
  setup() {
    const actorRef = useActorRef(someMachine);
    return actorRef;
  },
};
```

With options + listener:

```js
import { useInterpret } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';
export default {
  setup() {
    const actor = useInterpret(
      someMachine,
      {
        actions: {
          /* ... */
        },
      },
      (state) => {
        // subscribes to state changes
        console.log(state.value);
      },
    );
    // ...
  },
};
```

### `useSelector(actor, selector, compare?, getSnapshot?)`

A [Vue composition function](https://v3.vuejs.org/guide/composition-api-introduction.html) that returns the selected value from the snapshot of an `actorRef`, such as an actor. This hook will only cause a rerender if the selected value changes, as determined by the optional `compare` function.

#### Arguments

* `actorRef` - an actor or an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
* `selector` - a function that takes in an actor’s "current state" (snapshot) as an argument and returns the desired selected value.
* `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.
* `getSnapshot` (optional) - a function that should return the latest emitted value from the `actorRef`.
  * Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist. Will automatically pull the state from actors.

```js
import { useSelector } from '@xstate/vue';

const selectCount = (snapshot) => snapshot.context.count;

export default {
  props: ['actor'],
  setup(props) {
    const count = useSelector(props.actor, selectCount);
    // ...
    return { count };
  },
};
```

With `compare` function:

```js
import { useSelector } from '@xstate/vue';

const selectUser = (state) => state.context.user;
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;

export default {
  props: ['actor'],
  setup(props) {
    const user = useSelector(props.actor, selectUser, compareUser);
    // ...
    return { user };
  },
};
```

With `useActorRef(...)`:

```js
import { useActorRef, useSelector } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';

const selectCount = (snapshot) => snapshot.context.count;

export default {
  setup() {
    const actorRef = useActorRef(someMachine);
    const count = useSelector(actorRef, selectCount);
    // ...
    return { count, actorRef };
  },
};
```


# XState (/docs/xstate)

XState is a state management and orchestration solution for JavaScript and TypeScript apps.

It uses [event-driven](transitions) programming, [state machines, statecharts](state-machines-and-statecharts), and the [actor model](actor-model) to handle complex logic in predictable, robust, and visual ways. XState provides a powerful and flexible way to manage application and workflow state by allowing developers to model logic as actors and state machines. It integrates well with React, Vue, Svelte, and other frameworks and can be used in the frontend, backend, or wherever JavaScript runs.

<Callout>
  Want to find out more about state machines? [Read our introduction](state-machines-and-statecharts).
</Callout>

## Installation

XState is available on [npm](https://www.npmjs.com/package/xstate):

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install xstate
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install xstate
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add xstate
    ```
  </Tab>
</Tabs>

## Create a simple machine

```js
import { createMachine, assign, createActor } from 'xstate';

const countMachine = createMachine({
  context: {
    count: 0,
  },
  on: {
    INC: {
      actions: assign({
        count: ({ context }) => context.count + 1,
      }),
    },
    DEC: {
      actions: assign({
        count: ({ context }) => context.count - 1,
      }),
    },
    SET: {
      actions: assign({
        count: ({ event }) => event.value,
      }),
    },
  },
});

const countActor = createActor(countMachine).start();

countActor.subscribe((state) => {
  console.log(state.context.count);
});

countActor.send({ type: 'INC' });
// logs 1
countActor.send({ type: 'DEC' });
// logs 0
countActor.send({ type: 'SET', value: 10 });
// logs 10
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?machineId=89e9d8f1-73d6-4dee-92bc-1796270e2f50&mode=design" title="Simple machine" />

[Check out more cheatsheet examples](cheatsheet).

## Create a more complex machine

```js
import { createMachine, assign, createActor } from 'xstate';

const textMachine = createMachine({
  context: {
    committedValue: '',
    value: '',
  },
  initial: 'reading',
  states: {
    reading: {
      on: {
        'text.edit': { target: 'editing' },
      },
    },
    editing: {
      on: {
        'text.change': {
          actions: assign({
            value: ({ event }) => event.value,
          }),
        },
        'text.commit': {
          actions: assign({
            committedValue: ({ context }) => context.value,
          }),
          target: 'reading',
        },
        'text.cancel': {
          actions: assign({
            value: ({ context }) => context.committedValue,
          }),
          target: 'reading',
        },
      },
    },
  },
});

const textActor = createActor(textMachine).start();

textActor.subscribe((state) => {
  console.log(state.context.value);
});

textActor.send({ type: 'text.edit' });
// logs ''
textActor.send({ type: 'text.change', value: 'Hello' });
// logs 'Hello'
textActor.send({ type: 'text.commit' });
// logs 'Hello'
textActor.send({ type: 'text.edit' });
// logs 'Hello'
textActor.send({ type: 'text.change', value: 'Hello world' });
// logs 'Hello world'
textActor.send({ type: 'text.cancel' });
// logs 'Hello'
```

<EmbedMachine embedURL="https://stately.ai/registry/editor/embed/c447d996-cef1-421d-a422-8be695668764?mode=design&machineId=fa84c2d4-7c42-4f67-8bde-66f972133703" title="more complex machine" />

## Download the XState VS Code extension

<Callout>
  The XState VS Code extension does not fully support XState v5 yet.
</Callout>

* [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)
* [Open VSX Registry](https://open-vsx.org/extension/statelyai/stately-vscode)

[Read more about our developer tools](developer-tools).

## Packages

* 🤖 [xstate](https://github.com/statelyai/xstate/): Core finite state machine and statecharts library + interpreter, including graph traversal and model-based testing utilities
* ⚛️ [@xstate/react](https://github.com/statelyai/xstate/tree/main/packages/xstate-react): React hooks and utilities for using XState in React applications
* 💚 [@xstate/vue](https://github.com/statelyai/xstate/tree/main/packages/xstate-vue): Vue composition functions and utilities for using XState in Vue applications
* 🎷 [@xstate/svelte](https://github.com/statelyai/xstate/tree/main/packages/xstate-svelte): Svelte utilities for using XState in Svelte applications
* 🥏 [@xstate/solid](https://github.com/statelyai/xstate/tree/main/packages/xstate-solid)	Solid hooks and utilities for using XState in Solid applications
* 🔍 [@statelyai/inspect](https://github.com/statelyai/inspect)	Inspection utilities for XState
* 🏪 [@xstate/store](https://github.com/statelyai/xstate/tree/main/packages/xstate-store)	Small library for simple state management


# AI Agents (/docs/agents/agents)

<Callout type="warning">
  Stately Agent is still under development.
</Callout>

An AI agent is an autonomous entity that observes an environment, decides what to do (based on its internal policy), and performs actions towards achieving its goals. In terms of the actor model, an agent can be considered an actor that can:

* **Receive events**, such as an instruction on what to do next, which goal to accomplish, or an observation of the environment
* **Send events**, which would cause actions to be performed on the environment
* **Store state**, which can be used to remember contextual information about the environment
* **Spawn other agents**, which can be used to create a hierarchy of agents that can work together and coordinate their actions to achieve a goal

The [Stately Agent (`@statelyai/agent`)](https://github.com/statelyai/agent) package makes it simple to create agents and agent behavior based on the actor model and state machines. These agents can do much more than generate text and execute function calls; Stately Agent is a framework for:

* **Storing message history** between the user and assistant when using the generative text features
* **Making observations** of an environment, recording the transitions (previous state, event, next state) so it can understand the environment
* **Receiving feedback** on decisions it makes, so it can retrieve and corrolate feedback so that it can make more informed decisions
* **Making plans** in its decision-making progress, so that it not only predicts the very next decision to make, but a sequence of decisions that ideally reaches the goal
* **Short-term and long-term memory** for remembering message history, observations, feedback, and plans that it makes.

## Installation

Install the following dependencies:

* `@statelyai/agent@beta` – Stately.ai Agent, currently in beta
* `@ai-sdk/openai` – The Vercel AI SDK for OpenAI, which provides access to the OpenAI API
* `xstate` – Library for managing state machines and statecharts
* `zod` – Library for type-safe schema validation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @statelyai/agent @ai-sdk/openai xstate zod
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @statelyai/agent @ai-sdk/openai xstate zod
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @statelyai/agent @ai-sdk/openai xstate zod
    ```
  </Tab>
</Tabs>

## Quick start

1. Add your provider's API key to your `.env` file.

```bash
OPENAI_API_KEY="sk-abCDE..."
```

2. Create an agent.

```ts
import { openai } from '@ai-sdk/openai';
import { createAgent } from '@statelyai/agent';

const agent = createAgent({
  name: 'todo',
  model: openai('gpt-4-turbo'),
  events: {},
});
```

3. Add event schemas using Zod. These are the events that the agent is allowed to "cause" (i.e. send to the actor)

```ts
import { openai } from '@ai-sdk/openai';
import { createAgent } from '@statelyai/agent';
import { z } from 'zod';

const agent = createAgent({
  model: openai('gpt-4-turbo'),
  name: 'todo',
  // [!code highlight:18]
  events: {
    'todo.add': z.object({
      todo: z
        .object({
          title: z.string().describe('The title of the todo'),
          content: z.string().describe('The content of the todo'),
          completed: z
            .boolean()
            .describe('The completed value of the todo')
            .optional(),
        })
        .describe('Adds a new todo'),
    }),
    'todo.toggle': z.object({
      todoId: z.string().describe('The ID of the todo to toggle'),
      completed: z.boolean().describe('The new completed value').optional(),
    }),
  },
});
```

3. Interact with a [state machine actor](./state-machine-actors) that accepts those events.

```ts
import { setup, createActor } from 'xstate';
// [!code highlight:1]
import { agent } from './agent';

const todoMachine = setup({
  types: {
    // [!code highlight:2]
    // Add the event types that the agent understands
    events: agent.types.events,
  },
  // ...
}).createMachine({
  // ...
});

const todoActor = createActor(todoMachine);

// [!code highlight:1]
agent.interact(todoMachine);

todoActor.start();
```

## Creating an agent

You can create an agent using the `createAgent(settings)` function. There are required settings:

* `name` - The name of the agent, used for logging and agent learning purposes
* `model` - The [AI SDK language model](https://sdk.vercel.ai/docs/foundations/providers-and-models) to use for generating text and making tool calls
* `events` - A mapping of event types to [Zod](https://zod.dev/) event schemas that the agent can trigger (i.e. events it can send to some live environment that it is interacting with)

```ts
import { createAgent } from '@statelyai/agent';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const agent = createAgent({
  name: 'barista',
  model: openai('gpt-4-turbo'),
  events: {
    'barista.makeDrink': z
      .object({
        drink: z.enum(['espresso', 'latte', 'cappuccino']),
      })
      .describe('Makes a drink'),
    // ...
  },
});
```

You can specify additional settings to customize the agent's behavior:

* `description` - A description of the agent, used for logging and agent learning purposes, as well as for agents that call other agents (multi-agent systems)
* `planner` - An async function that takes the `agent` and planner `input` and resolves with an `AgentPlan` that potentially includes the `steps` and the `nextEvent` to execute to achieve the `input.goal`. This function is used to determine what the agent should do next when making a decision based on the current state (`input.state`) and goal (`input.goal`).
* `logic` - The agent logic function that is used to determine what an agent does when it receives an agent event, such as `"agent.feedback"`, `"agent.observe"`, `"agent.message"`, or `"agent.plan"`.

## Making decisions

The most important feature of a Stately agent is the ability to make decisions based on the current state and goal. This is done using the `agent.decide(input)` async function, which takes an `input` object that contains the current state, state machine, and goal, and resolves with an `AgentPlan`.

For example, suppose you have the following `baristaMachine` state machine:

```ts
import { createMachine } from 'xstate';

export const baristaMachine = createMachine({
  initial: 'idle',
  states: {
    idle: {
      on: {
        'barista.makeDrink': 'makingDrink',
      },
    },
    makingDrink: {
      on: {
        'barista.drinkMade': 'idle',
      },
    },
  },
});
```

You can then use the `agent.decide(input)` function to determine what the agent should do next:

```ts
import { createAgent } from '@statelyai/agent';
import { baristaMachine } from './baristaMachine';

const agent = createAgent({
  name: 'barista',
  model: openai('gpt-4-turbo'),
  events: {
    'barista.makeDrink': z
      .object({
        drink: z.enum(['espresso', 'latte', 'cappuccino']),
      })
      .describe('Makes a drink'),
  },
});

async function handleOrder(order, state) {
  const resolvedState = baristaMachine.resolveState(state);
  // [!code highlight:5]
  const plan = await agent.decide({
    state: resolvedState,
    machine: baristaMachine,
    goal: `A customer made this order: ${order}`,
  });

  return plan;
}

handleOrder('I want a latte please', { value: 'idle' });
// Resolves with an `AgentPlan` that includes:
// {
//   // ...
//   nextEvent: { type: 'barista.makeDrink', drink: 'latte' },
// }
```

## Agent memory

Stately agents can have two types of memory: **short-term (local) memory** and **long-term memory**.

* **Short-term (local) memory** is memory that can be synchronously retrieved, but might not be persisted.
* **Long-term memory** is memory that is asynchronously retrieved from persistent storage, such as a database.

Agents remember four kinds of things in their memory:

* **Messages** between the user and the assistant
* **Observations** of state transitions (previous state, event, current state) that occur in the environment that the agent is observing
* **Feedback**
* **Plans**

## Messages

### `agent.getMessages()`

Returns chat messages that occur between the user and the assistant from short-term memory.

### `agent.addMessage(message)`

If you want to manually add a message between the assistant and user to agent memory, you can call `agent.addMessage(message)` to do so. This is automatically called when calling `agent.generateText(…)`, `agent.streamText(…)`, or the `fromText(…)` and `fromTextStream(…)` actor logic creators. You should avoid calling this manually.

## Observations

### `agent.getObservations()`

Returns observations that the agent observes from short-term memory.

### `agent.addObservation(observation)`

You can add an observation (`{ prevState, event, state, … }`) to an agent's memory by calling `agent.addObservation(observation)`. This function returns an `AgentObservation` object that includes the provided observation details as well as an observation `id` so that the observation can be referenced in feedback, if applicable.

```ts
const observation = agent.addObservation({
  prevState: { value: 'idle', context: {} },
  event: { type: 'grindBeans' },
  state: { value: 'grindingBeans', context: {} },
});
```

## Feedback

### `agent.getFeedback()`

Returns feedback that is given to the agent from short-term memory.

### `agent.addFeedback(feedback)`

```ts
const observation = agent.addObservation({
  // ...
});

const feedback = agent.addFeedback({
  observationId: observation.id,
  goal: 'Make an iced coffee',
  attributes: {
    feedback: 'Water should not be boiled for an iced coffee',
    score: -10,
  },
});
```

## Plans

### `agent.getPlans()`

Returns plans that the agent has made from short-term memory.

### `agent.addPlan(plan)`

TODO

## Interacting with state machines

An agent can interact with existing state machine actors to determine what to do next. While the state machine actor is running, the agent will do the following cycle:

1. The agent **observes state changes**

* The observation is remembered in the agent's state

2. The agent **determines** if it needs to make a decision based on the current state
3. If it does, the agent **makes a decision** in the form of an `AgentPlan`.
4. If an `AgentPlan` is formed, the agent triggers the next event (`plan.nextEvent`) on the state machine actor.

* The plan is remembered in the agent's state.

4. The agent goes back to step 1, and the cycle continues.

```ts
import { createAgent } from '@statelyai/agent';
import { createActor } from 'xstate';
import { jokeMachine } from './jokeMachine';

const agent = createAgent({
  name: 'joke-teller',
  model: openai('gpt-4'),
  events: {
    'agent.tellJoke': z.object({
      joke: z.string().describe('The joke text'),
    }),
    'agent.rateJoke': z.object({
      rating: z.number().min(1).max(10),
      explanation: z.string(),
    }),
    // ...
  },
});

const jokeActor = createActor(jokeMachine).start();

agent.interact(jokeActor, (observed) => {
  if (observed.state.matches('tellingJoke')) {
    return { goal: `Tell a joke about ${observed.state.context.topic}` };
  }
  if (observed.state.matches('ratingJoke')) {
    return { goal: `Rate this joke: ${observed.state.context.joke}` };
  }
});
```

## State machine agents

You can invoke Stately agents as part of a state machine, ensuring that it will follow the state machine's transitions as specified and trigger the appropriate events. This is done by using any of the following [actor logic creators](./actors):

### `fromDecision(agent)`

Returns [promise actor logic](TODO) that resolves with the **agent plan** that should accomplish the goal (`input.goal`), if it is able to create one.

When invoked/spawned, this actor will also add the user and assistant messages to agent memory, as well as the plan that it created.

### `fromText(agent)`

Returns [promise actor logic](TODO) that resolves with the generated text result from the [Vercel AI SDK](https://sdk.vercel.ai/docs/reference/ai-sdk-core/generate-text#generatetext).

When invoked/spawned, this actor will also add the user and assistant messages to agent memory.

```ts
import { createAgent, fromText } from '@statelyai/agent';
import { setup } from 'xstate';

const agent = createAgent(/* ... */);

const machine = setup({
  actors: {
    assistant: fromText(agent),
  },
}).createMachine({
  initial: 'greeting',
  context: (x) => ({
    time: x.input.time,
  }),
  states: {
    greeting: {
      invoke: {
        src: 'assistant',
        input: ({ context }) => ({
          context: {
            time: context.time,
          },
          goal: 'Produce a greeting depending on the time of day.',
        }),
        onDone: {
          target: 'greeted',
          actions: ({ event }) => {
            console.log(event.output.text);
          },
        },
      },
    },
    greeted: {
      type: 'final',
    },
  },
});

const actor = createActor(machine, {
  input: { time: Date.now() },
});

actor.start();
```

### `fromTextStream(agent)`

Returns [observable actor logic](TODO) that streams the text from the [Vercel AI SDK](https://sdk.vercel.ai/docs/reference/ai-sdk-core/stream-text).

When invoked/spawned, this actor will also add the user and assistant messages to agent memory.

TODO: example

## Observability

* Can observe observations, plans, messages, and feedback via `agent.on('message', (message) => {})`
* Can manually add feedback observations via `agent.addFeedback(…)`

## Generating text

You can use the `agent.generateText(input)` method to generate text from an input. This extends the `generateText(…)` function from the [Vercel AI SDK](https://sdk.vercel.ai/docs/reference/ai-sdk-core/generate-text#generatetext) by:

* Adding the user and assistant messages to agent memory
* Providing the ability to retrieve previous observations, feedback, plans and messages from agent memory

## Streaming text

You can use the `agent.streamText(input)` method to stream text from an input. This extends the `streamText(…)` function from the [Vercel AI SDK](https://sdk.vercel.ai/docs/reference/ai-sdk-core/generate-text#generatetext) by:

* Adding the user and assistant messages to agent memory
* Providing the ability to retrieve previous observations, feedback, plans and messages from agent memory

## Agent logic

Agent logic is [actor logic](TODO) that has the specific purpose of performing LLM tasks for the agent. Agent logic goes beyond just being a wrapper and provides the ability to use the agent's state machine to intelligently determine which action to take next.

Agent logic is most powerful when used with a state-machine-powered agent, but you can also create standalone actors from agent logic, which is useful for testing and simple tasks.

## Examples

See the current examples in the [examples directory](https://github.com/statelyai/agent/tree/main/examples).


# @xstate/store-angular (/docs/xstate-store/angular)

The `@xstate/store-angular` package provides Angular bindings for [XState Store](/docs/xstate-store). It includes utilities for subscribing to store state as Angular signals.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-angular`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-angular
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-angular
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-angular
    ```
  </Tab>
</Tabs>

## Quick start

```ts
import { Component } from '@angular/core';
import { createStore, injectStore } from '@xstate/store-angular';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <div>
      <p>Count: {{ count() }}</p>
      <button (click)="store.trigger.inc()">+1</button>
      <button (click)="store.trigger.inc({ by: 5 })">+5</button>
    </div>
  `,
})
export class CounterComponent {
  store = store;
  count = injectStore(store, (state) => state.context.count);
}
```

## API

### `injectStore(store, selector?, compare?)`

Creates an Angular signal that subscribes to a store and returns a selected value.

```ts
import { Component } from '@angular/core';
import { injectStore } from '@xstate/store-angular';

@Component({
  selector: 'app-counter',
  template: `<div>Count: {{ count() }}</div>`,
})
export class CounterComponent {
  // Select specific value
  count = injectStore(store, (state) => state.context.count);

  // With custom comparison function
  user = injectStore(
    store,
    (state) => state.context.user,
    (prev, next) => prev.id === next.id
  );

  // Without selector (returns full snapshot)
  snapshot = injectStore(store);
}
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot. If not provided, returns the full snapshot.
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** A readonly Angular signal containing the selected value

<Callout>
  Remember to call the signal as a function (e.g., `count()`) in your template to access its value. This is how Angular's signal-based reactivity works.
</Callout>

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).


# @xstate/store-preact (/docs/xstate-store/preact)

The `@xstate/store-preact` package provides Preact bindings for [XState Store](/docs/xstate-store). It includes hooks for subscribing to store state, similar to the React bindings.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-preact`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-preact
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-preact
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-preact
    ```
  </Tab>
</Tabs>

## Quick start

```tsx
import { createStore, useSelector } from '@xstate/store-preact';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

function Counter() {
  const count = useSelector(store, (state) => state.context.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.trigger.inc()}>+1</button>
      <button onClick={() => store.trigger.inc({ by: 5 })}>+5</button>
    </div>
  );
}
```

## API

### `useSelector(store, selector?, compare?)`

Subscribes to a store and returns a selected value. The component re-renders when the selected value changes.

```tsx
import { useSelector } from '@xstate/store-preact';

function Counter() {
  // Select specific value
  const count = useSelector(store, (state) => state.context.count);

  // With custom comparison function
  const user = useSelector(
    store,
    (state) => state.context.user,
    (prev, next) => prev.id === next.id
  );

  return <div>Count: {count}</div>;
}
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** The selected value

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).


# @xstate/store-react (/docs/xstate-store/react)

The `@xstate/store-react` package provides React bindings for [XState Store](/docs/xstate-store). It includes hooks for subscribing to store state and creating local component stores.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-react`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-react
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-react
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-react
    ```
  </Tab>
</Tabs>

## Quick start

```tsx
import { createStore, useSelector } from '@xstate/store-react';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

function Counter() {
  const count = useSelector(store, (state) => state.context.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.trigger.inc()}>+1</button>
      <button onClick={() => store.trigger.inc({ by: 5 })}>+5</button>
    </div>
  );
}
```

## API

### `useSelector(store, selector?, compare?)`

Subscribes to a store and returns a selected value. The component re-renders when the selected value changes.

```tsx
import { useSelector } from '@xstate/store-react';

function Counter() {
  // Select specific value
  const count = useSelector(store, (state) => state.context.count);

  // With custom comparison function
  const user = useSelector(
    store,
    (state) => state.context.user,
    (prev, next) => prev.id === next.id
  );

  return <div>Count: {count}</div>;
}
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** The selected value

### `useStore(config)`

Creates a component-scoped store instance. Useful for local state management similar to `useReducer`.

```tsx
import { useStore, useSelector } from '@xstate/store-react';

function Counter({ initialCount = 0 }) {
  const store = useStore({
    context: { count: initialCount },
    on: {
      inc: (context) => ({ count: context.count + 1 }),
      dec: (context) => ({ count: context.count - 1 }),
    },
  });

  const count = useSelector(store, (state) => state.context.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.trigger.inc()}>+</button>
      <button onClick={() => store.trigger.dec()}>-</button>
    </div>
  );
}
```

**Parameters:**

* `config` - Store configuration object (same as `createStore`)

**Returns:** A store instance (stable across re-renders)

### `useAtom(atom, selector?, compare?)`

Subscribes to an atom and returns its value. Since v3.7.0.

```tsx
import { createAtom } from '@xstate/store-react';
import { useAtom } from '@xstate/store-react';

const countAtom = createAtom(0);

function Counter() {
  const count = useAtom(countAtom);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => countAtom.set((prev) => prev + 1)}>+</button>
    </div>
  );
}
```

**Parameters:**

* `atom` - The atom to subscribe to
* `selector` - Optional function to select a value from the atom
* `compare` - Optional comparison function

**Returns:** The (selected) atom value

### `createStoreHook(config)`

Creates a custom React hook that combines `useStore()` and `useSelector()`. Since v3.9.0.

```tsx
import { createStoreHook } from '@xstate/store-react';

const useCountStore = createStoreHook({
  context: { count: 0 },
  on: {
    inc: (ctx, event: { by: number }) => ({
      count: ctx.count + event.by,
    }),
  },
});

function Counter() {
  const [count, store] = useCountStore((s) => s.context.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.trigger.inc({ by: 1 })}>+1</button>
      <button onClick={() => store.trigger.inc({ by: 5 })}>+5</button>
    </div>
  );
}
```

**Parameters:**

* `config` - Store configuration object

**Returns:** A custom hook that returns `[selectedValue, store]`

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).


# @xstate/store-solid (/docs/xstate-store/solid)

The `@xstate/store-solid` package provides Solid bindings for [XState Store](/docs/xstate-store). It includes utilities for subscribing to store state as Solid signals.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-solid`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-solid
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-solid
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-solid
    ```
  </Tab>
</Tabs>

## Quick start

```tsx
import { createStore, useSelector } from '@xstate/store-solid';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

function Counter() {
  const count = useSelector(store, (state) => state.context.count);

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => store.trigger.inc()}>+1</button>
      <button onClick={() => store.trigger.inc({ by: 5 })}>+5</button>
    </div>
  );
}
```

## API

### `useSelector(store, selector?, compare?)`

Creates a Solid signal that subscribes to a store and returns a selected value.

```tsx
import { useSelector } from '@xstate/store-solid';

function Counter() {
  // Select specific value
  const count = useSelector(store, (state) => state.context.count);

  // With custom comparison function
  const user = useSelector(
    store,
    (state) => state.context.user,
    (prev, next) => prev.id === next.id
  );

  // Call the signal to get the value (Solid's reactivity model)
  return <div>Count: {count()}</div>;
}
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** A read-only Solid signal containing the selected value

<Callout>
  Remember to call the signal as a function (e.g., `count()`) to access its value. This is how Solid's reactivity system tracks dependencies.
</Callout>

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).


# @xstate/store-svelte (/docs/xstate-store/svelte)

The `@xstate/store-svelte` package provides Svelte bindings for [XState Store](/docs/xstate-store). It includes utilities for subscribing to store state as Svelte readable stores.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-svelte`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-svelte
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-svelte
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-svelte
    ```
  </Tab>
</Tabs>

## Quick start

```svelte
<script lang="ts">
  import { createStore, useSelector } from '@xstate/store-svelte';

  const store = createStore({
    context: { count: 0 },
    on: {
      inc: (context, event: { by?: number }) => ({
        count: context.count + (event.by ?? 1),
      }),
    },
  });

  const count = useSelector(store, (state) => state.context.count);
</script>

<div>
  <p>Count: {$count}</p>
  <button on:click={() => store.trigger.inc()}>+1</button>
  <button on:click={() => store.trigger.inc({ by: 5 })}>+5</button>
</div>
```

## API

### `useSelector(store, selector?, compare?)`

Creates a Svelte readable store that subscribes to an XState store and returns a selected value.

```svelte
<script>
  import { useSelector } from '@xstate/store-svelte';

  // Select specific value
  const count = useSelector(store, (state) => state.context.count);

  // With custom comparison function
  const user = useSelector(
    store,
    (state) => state.context.user,
    (prev, next) => prev.id === next.id
  );

  // Without selector (returns full snapshot)
  const snapshot = useSelector(store);
</script>

<!-- Use the $ prefix to access store value -->
<div>Count: {$count}</div>
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot. If not provided, returns the full snapshot.
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** A Svelte readable store containing the selected value

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).


# @xstate/store-vue (/docs/xstate-store/vue)

The `@xstate/store-vue` package provides Vue bindings for [XState Store](/docs/xstate-store). It includes composables for subscribing to store state as reactive refs.

<Callout>
  This package re-exports all of `@xstate/store`, so you only need to install `@xstate/store-vue`.
</Callout>

## Installation

<Tabs items={['npm', 'pnpm', 'yarn']}>
  <Tab value="npm" label="npm">
    ```bash
    npm install @xstate/store-vue
    ```
  </Tab>

  <Tab value="pnpm" label="pnpm">
    ```bash
    pnpm install @xstate/store-vue
    ```
  </Tab>

  <Tab value="yarn" label="yarn">
    ```bash
    yarn add @xstate/store-vue
    ```
  </Tab>
</Tabs>

## Quick start

```vue
<script setup>
import { createStore, useSelector } from '@xstate/store-vue';

const store = createStore({
  context: { count: 0 },
  on: {
    inc: (context, event: { by?: number }) => ({
      count: context.count + (event.by ?? 1),
    }),
  },
});

const count = useSelector(store, (state) => state.context.count);
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="store.trigger.inc()">+1</button>
    <button @click="store.trigger.inc({ by: 5 })">+5</button>
  </div>
</template>
```

## API

### `useSelector(store, selector?, compare?)`

Creates a reactive ref that subscribes to a store and returns a selected value.

```vue
<script setup>
import { useSelector } from '@xstate/store-vue';

// Select specific value
const count = useSelector(store, (state) => state.context.count);

// With custom comparison function
const user = useSelector(
  store,
  (state) => state.context.user,
  (prev, next) => prev.id === next.id
);

// Without selector (returns full snapshot)
const snapshot = useSelector(store);
</script>

<template>
  <div>Count: {{ count }}</div>
</template>
```

**Parameters:**

* `store` - The store to subscribe to
* `selector` - Optional function to select a value from the store snapshot. If not provided, returns the full snapshot.
* `compare` - Optional comparison function (defaults to strict equality `===`)

**Returns:** A read-only ref containing the selected value

## Full documentation

For complete XState Store documentation including context, transitions, effects, atoms, and more, see the [XState Store docs](/docs/xstate-store).