Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/statelyai/xstate/llms.txt

Use this file to discover all available pages before exploring further.

This page provides a comprehensive reference of all breaking changes introduced in XState v5. For a guided migration, see the v4 to v5 migration guide.

API Changes

interpret() → createActor()

interpret() has been completely removed. Use createActor() instead.
-import { interpret } from 'xstate';
+import { createActor } from 'xstate';

-const service = interpret(machine).start();
+const actor = createActor(machine).start();

String Events No Longer Supported

actorRef.send('EVENT') will throw an error. Events must be objects.
-actorRef.send('TOGGLE');
+actorRef.send({ type: 'TOGGLE' });

-actorRef.send('EVENT', { data: 'value' });
+actorRef.send({ type: 'EVENT', data: 'value' });

schema → types

The schema property is no longer recognized. Use types instead.
const machine = createMachine({
- schema: {
-   context: {} as { count: number }
- },
+ types: {} as {
+   context: { count: number };
+ },
  context: { count: 0 }
});

Context Property Required When Types Specified

If you define context types, you must provide an initial context value.
// ❌ Will cause TypeScript error
createMachine({
  types: {} as { context: { count: number } }
});

// ✅ Correct
createMachine({
  types: {} as { context: { count: number } },
  context: { count: 0 }
});

data → output

The data property on final states has been removed. Use output instead.
states: {
  success: {
    type: 'final',
-   data: { result: 42 }
+   output: { result: 42 }
  }
}

external → reenter

The external property on transitions has been renamed to reenter.
on: {
  EVENT: {
    target: 'sameState',
-   external: true
+   reenter: true
  }
}

in Property Removed

The in transition property has been removed. Use stateIn() guard instead.
+import { stateIn } from 'xstate/guards';

on: {
  EVENT: {
    target: 'target',
-   in: '#someState'
+   guard: stateIn('#someState')
  }
}

autoForward Removed

Auto-forwarding events is no longer supported. Explicitly forward events using sendTo().
invoke: {
  id: 'child',
  src: childMachine,
- autoForward: true
},
+on: {
+  FORWARD_THIS: {
+    actions: sendTo('child', (_, event) => event)
+  }
+}

machine.initialState Removed

machine.initialState has been removed. Use machine.getInitialState() instead.
-const initialState = machine.initialState;
+const initialState = machine.getInitialState();

machine.withConfig() → machine.provide()

withConfig() has been renamed to provide().
-const configuredMachine = machine.withConfig({
+const configuredMachine = machine.provide({
  actions: { /* ... */ }
});

machine.transition() Context Parameter Removed

The third parameter (context) has been removed from machine.transition().
-machine.transition('green', { type: 'TIMER' }, { elapsed: 100 });
+machine.transition(
+  machine.resolveState({ value: 'green', context: { elapsed: 100 } }),
+  { type: 'TIMER' }
+);

State/Snapshot Changes

State Properties Removed

The following properties have been removed from state/snapshot objects:
  • state.events - No replacement
  • state.nextEvents - No replacement
  • state.toStrings() - No replacement
  • state.activities - Activities are now part of invoke declarations

State Restoration API Changed

Restoring state now uses the snapshot option instead of passing state to start().
-interpret(machine).start(persistedState);
+createActor(machine, { snapshot: persistedSnapshot }).start();
Get persisted state:
-const state = actor.getSnapshot();
+const persistedSnapshot = actor.getPersistedSnapshot();

Restored State No Longer Contains Actions

Restored snapshots no longer include actions, as they’re assumed to have been executed.
If you need to replay actions, use event sourcing instead.

Actor/Service Changes

Removed Actor Methods

The following methods have been removed from actor references:
RemovedReplacement
actorRef.onEvent()actorRef.subscribe()
actorRef.onSend()actorRef.subscribe()
actorRef.onChange()actorRef.subscribe()
actorRef.onStop()actorRef.subscribe({ complete() {} })
actorRef.sender()actorRef.send()

send() No Longer Returns Next State

actorRef.send() is now void (fire-and-forget). It no longer returns the next state.
-const nextState = actorRef.send({ type: 'EVENT' });
+actorRef.send({ type: 'EVENT' });
+const nextState = actorRef.getSnapshot();

Callback Actors Cannot Return Promises

Returning promises from callback actors is no longer supported. Only cleanup functions (or undefined) can be returned.
fromCallback(({ sendBack }) => {
- return new Promise((resolve) => {
-   // ...
- });
+ const cleanup = () => {
+   // cleanup logic
+ };
+ return cleanup;
});

Behavior Changes

assign() Actions Execute in Order

assign() actions are no longer automatically prioritized. They execute in the order defined.
// In v5, these execute sequentially
entry: [
  assign({ count: ({ context }) => context.count + 1 }),
  assign({ doubled: ({ context }) => context.count * 2 }) // Uses updated count
]
To maintain v4 behavior, define all assign() actions before other actions.

Eventless Transitions Propagate Original Event

Actions following eventless transitions now receive the original event, not a null event { type: '' }.
states: {
  a: {
    on: { SOME_EVENT: 'b' }
  },
  b: {
    always: 'c'
  },
  c: {
    entry: ({ event }) => {
      // v4: event.type === ''
      // v5: event.type === 'SOME_EVENT'
    }
  }
}

No Re-entering on Internal Transitions

Atomic and parallel states are no longer re-entered on internal transitions by default.
Use reenter: true to get the previous behavior:
on: {
  SELF_TRANSITION: {
    target: 'sameState',
    reenter: true // Explicitly re-enter
  }
}

Compound States Must Specify initial

Compound state nodes without an initial property will now throw an error.
// ❌ Will throw error
states: {
  parent: {
    states: {
      child1: {},
      child2: {}
    }
  }
}

// ✅ Correct
states: {
  parent: {
    initial: 'child1',
    states: {
      child1: {},
      child2: {}
    }
  }
}

Delayed Event IDs No Longer Auto-Generated

Delayed events no longer derive IDs from event types. You must provide explicit IDs when using cancel().
-entry: raise({ type: 'TIMEOUT' }, { delay: 1000 });
-exit: cancel('TIMEOUT');
+entry: raise({ type: 'TIMEOUT' }, { delay: 1000, id: 'timeout' });
+exit: cancel('timeout');

Type System Changes

Context Must Be an Object

Machine context is now restricted to Record<string, any>. Primitive values are not allowed.
// ❌ Not allowed
createMachine({
  context: 'some string'
});

// ✅ Use an object
createMachine({
  context: { value: 'some string' }
});
If context is undefined, it defaults to {}.

Action Creators Return Functions

All built-in action creators now return functions. Their exact shape is an implementation detail.
import { assign } from 'xstate';

// Don't rely on the shape, just pass them to actions
const increment = assign({ count: ({ context }) => context.count + 1 });

tsTypes → types.typegen

The tsTypes property has been moved to types.typegen.
const machine = createMachine({
- tsTypes: {},
+ types: {} as {
+   typegen: {};
+   context: { /* ... */ };
+ }
});

Other Changes

Target Resolution from Root

Targeting sibling states from the root now requires explicit relative syntax.
createMachine({
  initial: 'a',
  states: { a: {}, b: {} },
  on: {
-   GO_TO_B: 'b'
+   GO_TO_B: '.b'
  }
});

execute Option Removed

The execute option for interpreted services has been removed.
Don’t hardcode implementation details in the base machine. Use machine.provide() to extend actions instead.

Dev Tools Integration Changed

Redux DevTools is no longer the default integration.
import { createActor } from 'xstate';
import { createReduxDevTools } from 'xstate/devTools/redux';

const actor = createActor(machine, {
  devTools: createReduxDevTools({ /* options */ })
});
Default dev tools attach to window.__xstate__:
const actor = createActor(machine, {
  devTools: true
});

Context Function Removed

The context property no longer accepts a function for determining initial context.
Use the input parameter in createActor() instead:
const machine = createMachine({
  context: ({ input }) => ({
    value: input.initialValue
  })
});

const actor = createActor(machine, {
  input: { initialValue: 42 }
});

State Node Getters Removed

Support for getters as transition targets has been removed.
Use state node IDs or relative keys instead.

Machine() No Longer Accepts Context as Third Argument

The Machine() and createMachine() functions no longer support a third argument for context.
-const machine = createMachine(config, options, context);
+const machine = createMachine({ ...config, context });

Migration Resources

Migration Guide

Step-by-step guide to migrating from v4 to v5

Getting Help

Get help with your migration on Discord or GitHub

Build docs developers (and LLMs) love