# XState changelog > State machines and statecharts for application logic in JavaScript. - Vendor: Stately - Category: Frameworks & Libraries - Official site: https://stately.ai/docs - Tracked by: What's New (https://whatsnew.fyi/product/xstate) - Harvested from: GitHub (statelyai/xstate) - Entries below: 25 (newest first) What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. Reuse: the summaries, labels and curation here are © What's New. Quote freely with attribution and a link back; wholesale republication of the corpus is not permitted — terms: https://whatsnew.fyi/terms. The vendors' own release notes remain their publishers'. ## Releases ### 5.32.5 - Date: 2026-07-14 - Version: 5.32.5 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.5 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.5 - **fixed** — Sending an event to a stopped actor no longer throws when the event contains unserializable data; the warning is now emitted safely regardless of the event's contents ###### Patch Changes - [#5603](https://github.com/statelyai/xstate/pull/5603) [`345e04c`](https://github.com/statelyai/xstate/commit/345e04ce66963c2a5a2a879bb4928a1b179de7f6) Thanks [@xianjianlf2](https://github.com/xianjianlf2)! - Sending an event to a stopped actor no longer throws when the event contains unserializable data (e.g. circular references). Previously, the development-only warning that an event was sent to a stopped actor used `JSON.stringify` on the event, which could throw and mask the intended warning. The warning is now emitted safely regardless of the event's contents. ### 5.32.4 - Date: 2026-07-02 - Version: 5.32.4 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.4 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.4 - **fixed** — Fixed a bug where targeting a history state that is a direct child of a parallel state would silently do nothing when that parallel state had not been visited yet and the history state had no default target; the machine now enters the parallel state's initial configuration, matching the behavior of history states inside compound states ###### Patch Changes - [#5589](https://github.com/statelyai/xstate/pull/5589) [`e913eeb`](https://github.com/statelyai/xstate/commit/e913eebb655b55440b2971f791fbf079a0e5d7ad) Thanks [@spokodev](https://github.com/spokodev)! - Fixed a bug where targeting a history state that is a direct child of a `parallel` state would silently do nothing when that parallel state had not been visited yet and the history state had no default target. The machine now enters the parallel state's initial configuration, matching the behavior of history states inside compound states. ```ts const machine = createMachine({ initial: 'off', states: { off: { on: { GO: 'on.hist' } }, on: { type: 'parallel', states: { regA: { initial: 'a1', states: { a1: {}, a2: {} } }, regB: { initial: 'b1', states: { b1: {}, b2: {} } }, hist: { type: 'history', history: 'deep' } } } } }); const actor = createActor(machine).start(); actor.send({ type: 'GO' }); actor.getSnapshot().value; // { on: { regA: 'a1', regB: 'b1' } } ``` ### 6.0.0-alpha.16 - Date: 2026-07-02 - Version: 6.0.0-alpha.16 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.16 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.16 - Labels: Pre-release - **added** — Add state-level onError transitions for handling xstate.error.* events, with the caught error available on event.error - **changed** — Allow machines with no external events to be used anywhere AnyActorLogic or AnyStateMachine is expected ###### Minor Changes - e410f24: Add state-level `onError` transitions for handling `xstate.error.*` events. State `onError` catches actor, execution, and communication errors while the state is active. The caught error is available on `event.error`. ```ts const machine = createMachine({ initial: 'active', states: { active: { onError: ({ event }) => ({ target: 'failed', context: { message: event.error instanceof Error ? event.error.message : String(event.error) } }) }, failed: {} } }); ``` ###### Patch Changes - f6edec7: Allow machines with no external events to be used anywhere `AnyActorLogic` or `AnyStateMachine` is expected. ```ts const machine = setup({ schemas: { events: {} } }).createMachine({}); const logic: AnyActorLogic = machine; const anyMachine: AnyStateMachine = machine; ``` Machines with empty event schemas still reject external events sent to their actors. ### 5.32.3 - Date: 2026-07-01 - Version: 5.32.3 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.3 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.3 - **fixed** — Fixed initialTransition and transition throwing "Actor with system ID '...' already exists" error when the machine contains an invoke with a systemId by replacing the actor's system reference with a freshly-created system in createInertActorScope - **fixed** — Add missing https:// protocol to the Stately Studio link in the README ###### Patch Changes - [#5575](https://github.com/statelyai/xstate/pull/5575) [`830db8b`](https://github.com/statelyai/xstate/commit/830db8b6b4ac81331bdcae44aa7ec522fa959960) Thanks [@JSap0914](https://github.com/JSap0914)! - Fixed `initialTransition` (and `transition`) throwing `"Actor with system ID '...' already exists"` when the machine contains an `invoke` with a `systemId`. **Root cause:** `createInertActorScope` used `createActor(logic)` internally, which eagerly ran `getInitialSnapshot` during construction and registered any `systemId`-carrying child actors in the system. When the caller then ran `getInitialSnapshot` (or `transition`) via the returned scope, the same system was reused, causing the duplicate-registration error. **Fix:** After creating the internal actor, `createInertActorScope` now replaces the actor's system reference with a freshly-created system. Child actors spawned by the subsequent caller-driven `getInitialSnapshot` / `transition` invocation therefore register into a clean system with no pre-existing entries. ```ts const machine = createMachine({ initial: 'idle', states: { idle: { invoke: { src: fromPromise(async () => 42), systemId: 'myActor' // previously caused: "Actor with system ID 'myActor' already exists" } } } }); // Now works correctly — returns [snapshot, actions] without throwing const [snapshot, actions] = initialTransition(machine); ``` - [#5585](https://github.com/statelyai/xstate/pull/5585) [`a551a2b`](https://github.com/statelyai/xstate/commit/a551a2b81fbbe05f8ffc5b3cdaea503d01ce477e) Thanks [@RubenFricke](https://github.com/RubenFricke)! - Add missing https:// protocol to the Stately Studio link in the README ### 6.0.0-alpha.15 - Date: 2026-07-01 - Version: 6.0.0-alpha.15 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.15 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.15 - Labels: Pre-release - **added** — Setup-bound invoke transition callbacks now validate target state context requirements for onDone, onError, onSnapshot, and onTimeout - **added** — onDone callback now infers output from the invoked actor logic ###### Patch Changes - 37d3254: Setup-bound invoke transition callbacks now validate target state context requirements for `onDone`, `onError`, `onSnapshot`, and `onTimeout`. `onDone` also infers output from the invoked actor logic. ```ts import { createAsyncLogic, setup } from 'xstate'; import { z } from 'zod'; const machine = setup({ actorSources: { loadUser: createAsyncLogic({ run: async () => ({ name: 'Ada' }) }) }, states: { loading: {}, success: { schemas: { context: z.object({ user: z.object({ name: z.string() }) }) } } } }).createMachine({ context: {}, initial: 'loading', states: { loading: { invoke: { src: 'loadUser', // Type-safe return value for invoke callbacks onDone: ({ event }) => ({ target: 'success', context: { user: event.output } }) } }, success: {} } }); ``` ### 6.0.0-alpha.14 - Date: 2026-07-01 - Version: 6.0.0-alpha.14 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.14 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.14 - Labels: Pre-release - **added** — Add a path-bound overload to `setup(...).createStateConfig(path, config)` that binds the config to a specific setup-declared state by dotted path and narrows the input schema accordingly - **fixed** — Fix `snapshot.matches(...)` narrowing so repeated checks like `snapshot.matches('loaded') || snapshot.matches('failed')` compile correctly - **changed** — Make `StateFrom` preserve the machine's concrete state value - **fixed** — Fix function-syntax transitions to pass `input` to target state entry actions ###### Minor Changes - c0c21d0: Add a path-bound overload to `setup(...).createStateConfig(path, config)`. When a state declares its own input schema, the anonymous `createStateConfig(config)` form types `input` as a broad union across all states. This makes the resulting config incompatible with the specific state it's meant for — assigning it inside `createMachine` produces a type error because the input types don't match. The new `createStateConfig(path, config)` overload binds the config to a specific setup-declared state by dotted path (e.g. `'loading'` or `'parent.child'`). The addressed state's own input schema is used inside `entry`/`exit` args, and bare transition targets are validated against the state's siblings. ```ts const s = setup({ states: { idle: {}, active: { schemas: { input: z.object({ userId: z.string() }) } } } }); // Before: anonymous form — `input` is typed broadly, and assigning this // config to the `active` state in createMachine fails with a type error. const active = s.createStateConfig({ entry: ({ input }) => { // input is not narrowed to { userId: string } } }); // After: path-bound form — `input` is narrowed to `active`'s own schema. const active = s.createStateConfig('active', { entry: ({ input }) => { input.userId; // string } }); // Works for nested states too: const child = s.createStateConfig('parent.child', { ... }); ``` ###### Patch Changes - 8e3cce6: Fix `snapshot.matches(...)` narrowing so repeated checks like `snapshot.matches('loaded') || snapshot.matches('failed')` compile correctly, and make `StateFrom` preserve the machine's concrete state value. - 0c2a6e5: Fix function-syntax transitions not passing `input` to target state entry actions. ```ts on: { FETCH: ({ context, event }) => ({ target: 'fetching', input: { url: event.url, token: context.authToken } }); } ``` Previously, `input` returned from function-syntax transitions was silently ignored. Now it is correctly forwarded to the target state's `entry` action. ### 6.0.0-alpha.13 - Date: 2026-06-30 - Version: 6.0.0-alpha.13 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.13 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.13 - Labels: Pre-release - **added** — Added `createFSM(...)` for flat, actor-compatible finite state machines with support for object transitions, function transitions, `enq` actions, initial input, state `input`, entry actions, and exit actions - **changed** — Object transition configs now support dynamic context patches with a `context` mapper function ###### Minor Changes - bdc54dd: Added `createFSM(...)` for flat, actor-compatible finite state machines. ```ts import { createActor, createFSM } from 'xstate'; const toggleLogic = createFSM({ initial: 'inactive', context: { count: 0 }, states: { inactive: { on: { toggle: { target: 'active', context: { count: 1 } } } }, active: { on: { toggle: ({ context }, enq) => { enq(() => console.log('toggled')); return { target: 'inactive', context: { count: context.count + 1 } }; } } } } }); const actor = createActor(toggleLogic).start(); actor.send({ type: 'toggle' }); ``` `createFSM(...)` supports XState-style object transitions, function transitions, `enq` actions, initial input, state `input`, entry actions, and exit actions. Plain string targets are intentionally not supported; use object targets such as `{ target: 'active' }`. Simple FSM transitions preserve immutable public snapshots while using structural sharing and a lighter transition path for common `{ target }`, `{ context }`, and `{ target, context }` transitions. ###### Patch Changes - e297115: Object transition configs now support dynamic context patches with a `context` mapper. ```ts onDone: { target: 'done', context: ({ context, output }) => ({ answer: output, memory: [...context.memory, output] }) } ``` ### 6.0.0-alpha.12 - Date: 2026-06-29 - Version: 6.0.0-alpha.12 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.12 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.12 - Labels: Pre-release - **fixed** — Spawning a child with enq.spawn(...) from a transition function now creates and starts the child actor exactly once for the committed transition - **changed** — serializeMachine(...) and createMachineFromConfig(...) now represent inline functions as { '@code': string, '@lang': 'ts' } and omit non-portable values such as actor logic, runtime schemas, class instances, symbols, and bigints from serialized JSON - **changed** — void and undefined are now accepted as type-only schemas - **changed** — Async logic output is now inferred from an input-only schema - **changed** — Actions and guards can now be typed via schemas - **changed** — trigger is now correctly typed on spawned actors ###### Patch Changes - 4d9ba1c: Spawning a child with `enq.spawn(...)` from a transition function now creates and starts the child actor exactly once for the committed transition. ```ts const machine = createMachine({ on: { spawn: (_, enq) => { enq.spawn(childMachine, { registryKey: 'child' }); } } }); ``` - 6798cb1: `serializeMachine(...)` and `createMachineFromConfig(...)` now represent inline functions (guards, actions, transitions, delays, route functions) as `{ '@code': string, '@lang': 'ts' }`. Non-portable values such as actor logic, runtime schemas, class instances, symbols, and bigints are omitted from the serialized JSON. ```ts import { serializeMachine } from 'xstate'; serializeMachine(machine); // inline functions → { '@code': '() => true', '@lang': 'ts' } ``` Type-only refinements: `void` and `undefined` are accepted as type-only schemas, async logic output is inferred from an input-only schema, actions/guards can be typed via `schemas`, and `trigger` is correctly typed on spawned actors. ### 6.0.0-alpha.11 - Date: 2026-06-28 - Version: 6.0.0-alpha.11 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.11 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.11 - Labels: Pre-release - **changed** — Machines that declare schemas.output now type-check top-level final state output values against the machine output type ###### Patch Changes - 57e8d85: Machines that declare `schemas.output` now type-check top-level final state `output` values against the machine output type. ```ts createMachine({ schemas: { output: types<{ status: 'ok' }>() }, initial: 'done', states: { done: { type: 'final', output: { status: 'ok' } } }, output: ({ event }) => event.output }); ``` ### 6.0.0-alpha.10 - Date: 2026-06-27 - Version: 6.0.0-alpha.10 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.10 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.10 - Labels: Pre-release - **fixed** — Export setup system helper types used by public machine types to avoid inferred machine types referring to internal declaration paths when setup(...) includes a typed system registry ###### Patch Changes - 86b43ea: Export setup system helper types used by public machine types. This avoids inferred machine types referring to internal declaration paths when `setup(...)` includes a typed system registry. ### 6.0.0-alpha.9 - Date: 2026-06-26 - Version: 6.0.0-alpha.9 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.9 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.9 - Labels: Pre-release - **added** — Export setup helper types AnySetupConfig and SetupReturnFromConfig for libraries that return or decorate setup-bound objects while preserving native setup(...).createMachine(...) typing - **fixed** — Empty Standard Schema event objects now infer as type-only events, so { type: 'SEND' } is accepted for an empty SEND payload schema while non-empty schemas still require their payload fields - **changed** — Registered invoke onDone callbacks now receive the invoked actor's output type, and machine.provide({ actorSources }) accepts compatible actor implementations with sound input/output variance ###### Minor Changes - 54205cc: Export setup helper types for libraries that return or decorate setup-bound objects while preserving native `setup(...).createMachine(...)` typing. ```ts import { setup, type AnySetupConfig, type SetupReturnFromConfig } from 'xstate'; function decorateSetup( config: TConfig ): SetupReturnFromConfig & { extra: true } { const s = setup(config) as SetupReturnFromConfig; return Object.assign(s, { extra: true as const }); } ``` ###### Patch Changes - 54205cc: Empty Standard Schema event objects now infer as type-only events, so `{ type: 'SEND' }` is accepted for an empty `SEND` payload schema while non-empty schemas still require their payload fields. - 54205cc: Registered invoke `onDone` callbacks now receive the invoked actor's output type, and `machine.provide({ actorSources })` accepts compatible actor implementations with sound input/output variance. ### 6.0.0-alpha.8 - Date: 2026-06-25 - Version: 6.0.0-alpha.8 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.8 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.8 - Labels: Pre-release - **changed** — Done transitions now receive `output` directly in callback arguments for XState done events ###### Patch Changes - 667d1c7: Done transitions now receive `output` directly in callback arguments. ```ts invoke: { src: fetchUser, onDone: ({ output }) => { output.name; } } ``` The direct `output` value is only provided for XState done events, such as `xstate.done.actor.*` and `xstate.done.state.*`. ### 6.0.0-alpha.7 - Date: 2026-06-25 - Version: 6.0.0-alpha.7 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.7 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.7 - Labels: Pre-release - **added** — Add `createSystem({ registry })` for declaring typed actor registry keys and creating actors in that system, with registry keys assigned via `registryKey` on invokes, spawned actors, and root actors - **added** — Static transition config objects may now include a shallow `context` patch that is shallow-merged with the current context ###### Major Changes - 89895f9: Add `createSystem({ registry })` for declaring typed actor registry keys and creating actors in that system. Registry keys are assigned with `registryKey` on invokes, spawned actors, and root actors created from the system. Registry keys are checked against the declared registry when using `createSystem`. ```ts const system = createSystem({ registry: { receiver: receiverLogic } }); const machine = system.setup().createMachine({ invoke: { src: receiverLogic, registryKey: 'receiver' } }); const actor = system.createActor(machine).start(); system.get('receiver')?.send({ type: 'HELLO' }); ``` ###### Patch Changes - 89895f9: Static transition config objects may now include a shallow `context` patch. ```ts createMachine({ context: { draftAnyway: false, count: 0 }, initial: 'idle', states: { idle: { on: { DRAFT_ANYWAY: { target: 'drafting', context: { draftAnyway: true } } } }, drafting: {} } }); ``` The patch is shallow-merged with the current context, just like `context` returned from a transition function. Setup-typed machines still require any keys needed by the target state's narrowed context. ### 6.0.0-alpha.6 - Date: 2026-06-24 - Version: 6.0.0-alpha.6 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.6 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.6 - Labels: Pre-release - **changed** — State transition functions now type `enq` with the machine's events and emitted events - **changed** — Transition functions may now return only a target when the target state's context is compatible with the current context ###### Patch Changes - ecd97db: State transition functions now type `enq` with the machine's events and emitted events. ```ts setup({ schemas: { events: { go: types<{}>() } } }).createMachine({ states: { active: { on: { go: (_args, enq) => { enq.raise({ type: 'go' }); } } } } }); ``` - 4b5b14f: Transition functions may now return only a target when the target state's context is compatible with the current context. ```ts setup({ schemas: { context: types<{ count: number }>(), events: { next: types<{}>() } } }).createMachine({ context: { count: 0 }, initial: 'idle', states: { idle: { on: { next: () => ({ target: 'done' }) } }, done: {} } }); ``` ### 6.0.0-alpha.5 - Date: 2026-06-24 - Version: 6.0.0-alpha.5 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.5 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.5 - Labels: Pre-release - **removed** — String target shorthand is no longer accepted for transition configs; use the object form with `target` property instead ###### Major Changes - 297f851: String target shorthand is no longer accepted for transition configs. Use the object form with `target` instead: ```ts createMachine({ initial: 'idle', states: { idle: { on: { start: { target: 'active' } } }, active: {} } }); ``` ### 6.0.0-alpha.4 - Date: 2026-06-23 - Version: 6.0.0-alpha.4 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.4 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.4 - Labels: Pre-release - **changed** — Actor logic now returns effects from both regular and initial transitions, with transition(...) and initialTransition(...) returning [snapshot, effects] tuples - **added** — Add schemas.children for explicitly typing child actor refs by child ID with declared child refs type, child snapshots, and invoke configs - **added** — Add isBuiltInExecutableAction(...) to narrow executable effects to XState's built-in effect union for declarative inspection - **changed** — Built-in executable effects now expose stable named metadata fields accessible via effect.type including @xstate.start, @xstate.sendTo, @xstate.raise, and @xstate.stop - **changed** — fromStore(...) effects now run after the actor snapshot is committed so effect callbacks read the updated snapshot from enqueue.getSnapshot() ###### Major Changes - c3f7a9d: Actor logic now returns effects from both regular and initial transitions. Hand-written actor logic should return `[snapshot, effects]` from `transition(...)` and provide `initialTransition(...)` for creating the initial `[snapshot, effects]` tuple. `getInitialSnapshot(...)` remains available for snapshot-only reads. ```ts const logic = { transition: (snapshot, event) => [snapshot, []], initialTransition: (input, _scope) => [ { status: 'active', output: undefined, error: undefined, input }, [] ], getInitialSnapshot: (scope, input) => logic.initialTransition(input, scope)[0] }; ``` `transition(...)` and `initialTransition(...)` continue to return `[snapshot, actions]` for machine logic. `fromStore(...)` effects now run after the actor snapshot is committed, so effect callbacks read the updated snapshot from `enqueue.getSnapshot()`. - 309b106: Add `schemas.children` for explicitly typing child actor refs by child ID. Declared child refs type `children.someId`, child snapshots, and invoke configs so `invoke: { id: 'someId', src }` must match the declared child actor contract. - fa2bbf0: Built-in executable effects returned from `transition(...)` and `initialTransition(...)` are now easier to inspect declaratively. Use `isBuiltInExecutableAction(effect)` to narrow an executable effect to XState's built-in effect union, then switch on `effect.type` to access stable, named metadata fields: ```ts const [snapshot, effects] = initialTransition(machine); for (const effect of effects) { if (!isBuiltInExecutableAction(effect)) { continue; } switch (effect.type) { case '@xstate.start': effect.id; effect.logic; effect.src; effect.input; break; case '@xstate.sendTo': effect.target; effect.event; effect.delay; break; case '@xstate.raise': effect.event; effect.delay; break; } } ``` The built-in stop effect is now exposed as `@xstate.stop`, matching `@xstate.start`. ### 5.32.2 - Date: 2026-06-23 - Version: 5.32.2 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.2 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.2 - **fixed** — Fall back to wildcard event descriptors when an exact event descriptor's guard fails, allowing wildcard transitions to be considered if exact match guards fail ###### Patch Changes - [#5548](https://github.com/statelyai/xstate/pull/5548) [`8a53531`](https://github.com/statelyai/xstate/commit/8a5353105522aaee7a4f7031136372edec327a79) Thanks [@JSap0914](https://github.com/JSap0914)! - fix(core): fall back to wildcard event descriptors when an exact descriptor's guard fails When a state has both an exact event descriptor (e.g. `"foo.bar"`) and a matching wildcard descriptor (e.g. `"foo.*"`), transitions from the exact descriptor are now tried first; if all their guards fail, matching wildcard descriptor transitions are tried as fallback. Previously, the presence of an exact match would prevent any wildcard fallback from being considered, leaving the machine in its current state when the exact descriptor's guard failed. ### 6.0.0-alpha.3 - Date: 2026-06-22 - Version: 6.0.0-alpha.3 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.3 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.3 - Labels: Pre-release - **added** — Expose machine.schemas as a public runtime-readable schema contract - **changed** — Serialize function implementations as portable code expressions in guards, actions, delays, and inline machine config ###### Minor Changes - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`0a883ad`](https://github.com/statelyai/xstate/commit/0a883ad8827f21faed58079179d51cd6ec662b55) Thanks [@pull](https://github.com/apps/pull)! - Expose `machine.schemas` as a public runtime-readable schema contract. ```ts const machine = createMachine({ schemas: { context: z.object({ count: z.number() }), events: { inc: z.object({ by: z.number() }) } }, context: { count: 0 } }); machine.schemas?.events?.inc; ``` ###### Patch Changes - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`d95287b`](https://github.com/statelyai/xstate/commit/d95287bc9a05cca56ff3c887c321e64721a137ed) Thanks [@pull](https://github.com/apps/pull)! - Serialize function implementations as portable code expressions. Functions in guards, actions, delays, and inline machine config now serialize as: ```ts { '@type': 'code', lang: 'ts', expr: '() => true' } ``` Values that cannot be represented as code or JSON still serialize with explicit `$unserializable` markers. ### 6.0.0-alpha.2 - Date: 2026-06-21 - Version: 6.0.0-alpha.2 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.2 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.2 - Labels: Pre-release - **fixed** — Fixed a bug where an invoked actor's input and dynamic src function received the context from before the transition that entered the invoking state, rather than the updated context ###### Patch Changes - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`d6a537e`](https://github.com/statelyai/xstate/commit/d6a537eeb078be3256ac269102e28a8aad2f5403) Thanks [@pull](https://github.com/apps/pull)! - Fixed a bug where an invoked actor's `input` (and a dynamic `src` function) received the context from _before_ the transition that entered the invoking state, rather than the updated context. Now, when a transition updates context and targets a state that invokes an actor, the actor's `input` sees the updated context — consistent with that state's `entry` actions. ```ts const machine = createMachine({ context: { value: 0 }, initial: 'idle', states: { idle: { on: { start: () => ({ target: 'active', context: { value: 100 } }) } }, active: { invoke: { src: asyncLogic, // now receives { value: 100 } instead of { value: 0 } input: ({ context }) => ({ val: context.value }) } } } }); ``` ### 6.0.0-alpha.1 - Date: 2026-06-20 - Version: 6.0.0-alpha.1 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.1 - Permalink: https://whatsnew.fyi/product/xstate/releases/6.0.0-alpha.1 - Labels: Pre-release - **changed** — Invoked and spawned actors now start as part of the transition that creates them via an internal deferred start action instead of being started directly by actor.start() - **changed** — Child failures that occur synchronously while starting now surface through the invoking state's onError transition instead of throwing out of actor.start() - **removed** — Remove action and guard creators: assign, raise, sendTo, sendParent, forwardTo, emit, log, cancel, spawnChild, stop, stopChild, enqueueActions, and guard creators and, or, not, stateIn - **changed** — Actions, guards, and transitions are now plain inline functions that receive args and an enqueue object for side effects - **changed** — Update context by returning a partial or full context patch instead of using assign - **changed** — Perform side effects through the enqueue object with methods: raise, sendTo, emit, log, cancel, spawn, stop, and arbitrary effects via enq(fn, ...args) - **changed** — Guards are now plain functions that return a boolean instead of using guard creators - **changed** — Replace stateIn guard by checking the snapshot directly using snapshot.matches() inside a transition function - **added** — Export checkStateIn(snapshot, '#id') helper for matching state by id - **removed** — Remove deprecated interpret function and Interpreter type - **changed** — Use createActor(machine) and Actor type instead of interpret(machine) and Interpreter - **changed** — Replace schemas as the primary way to type a machine, supporting Standard Schema (Zod, Valibot, etc) for both type inference and runtime validation - **changed** — schemas.events is now a map of event-type to payload schema instead of a union type - **changed** — schemas.context types context values with literal initial values widened for update typechecking - **changed** — schemas.input types createActor(machine, { input }) and the context initializer argument - **changed** — schemas.output types snapshot.output - **changed** — schemas.emitted types actor.on() callbacks with emitted event payloads - **changed** — schemas.tags constrains snapshot.hasTag() parameter values - **changed** — schemas.meta types state meta values - **changed** — Move actors, actions, guards, and delays to top-level config keys instead of schemas keys ###### Major Changes - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`52970ea`](https://github.com/statelyai/xstate/commit/52970ea75489305fd7bf1223f9b413770cd6d925) Thanks [@pull](https://github.com/apps/pull)! - Invoked and spawned actors are no longer started directly by `actor.start()`. They now start as part of the transition that creates them (via an internal deferred start action), the same way other entry effects run. The user-visible consequence: a child that fails synchronously while starting now surfaces that failure through the invoking state's `onError` transition instead of throwing out of `actor.start()`: ```ts const machine = createMachine({ initial: 'loading', states: { loading: { invoke: { src: createAsyncLogic({ run: () => { throw new Error('boom'); // sync failure on start } }), onError: 'failed' } }, failed: {} } }); const actor = createActor(machine).start(); // does not throw actor.getSnapshot().value; // 'failed' ``` Restored (rehydrated) children that were active when a snapshot was persisted are still restarted on `actor.start()`, so persistence behavior is unchanged. - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`52970ea`](https://github.com/statelyai/xstate/commit/52970ea75489305fd7bf1223f9b413770cd6d925) Thanks [@pull](https://github.com/apps/pull)! - Actions, guards, and transitions are now plain inline functions, and the v5 action/guard creators are removed. Removed exports: `assign`, `raise`, `sendTo`, `sendParent`, `forwardTo`, `emit`, `log`, `cancel`, `spawnChild`, `stop`, `stopChild`, `enqueueActions`, and the guard creators `and`, `or`, `not`, `stateIn`. Instead, a transition/action/guard is a function `(args, enq) => ...`: - Update context by **returning** a partial-or-full `{ context }` patch (no more `assign`). - Perform side effects through the `enq` enqueue object: `enq.raise`, `enq.sendTo`, `enq.emit`, `enq.log`, `enq.cancel`, `enq.spawn`, `enq.stop`, plus `enq(fn, ...args)` for arbitrary effects. - Guards are just functions that return a boolean (or `undefined`/`false` to block). ```diff - import { assign, raise, sendTo, and, not } from 'xstate'; const machine = createMachine({ context: { count: 0 }, on: { - INC: { - guard: and([not('isMax'), 'isReady']), - actions: assign({ count: ({ context }) => context.count + 1 }) - } + INC: ({ context, guards }) => { + if (guards.isMax(context) || !guards.isReady(context)) return; + return { context: { count: context.count + 1 } }; + } } }); ``` The `stateIn` guard is replaced by checking the snapshot directly — use `snapshot.matches(...)` inside a transition function: ```ts on: { CHECK: ({ self }) => { if (self.getSnapshot().matches({ b: 'b2' })) { return { target: 'a2' }; } }; } ``` For matching by state **id** (the `'#id'` form, which `matches()` doesn't resolve), the exported `checkStateIn(snapshot, '#id')` helper is also available. - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`52970ea`](https://github.com/statelyai/xstate/commit/52970ea75489305fd7bf1223f9b413770cd6d925) Thanks [@pull](https://github.com/apps/pull)! - Remove the deprecated `interpret` function and `Interpreter` type. Use `createActor(...)` and `Actor` (or `ActorRefFrom<...>`) instead. ```diff - import { interpret, type Interpreter } from 'xstate'; - const actor = interpret(machine); + import { createActor, type Actor } from 'xstate'; + const actor = createActor(machine); ``` - [#44](https://github.com/balrog-typescript/xstate/pull/44) [`52970ea`](https://github.com/statelyai/xstate/commit/52970ea75489305fd7 _[Truncated at 4000 characters — full notes: https://github.com/statelyai/xstate/releases/tag/xstate%406.0.0-alpha.1]_ ### 5.32.1 - Date: 2026-06-12 - Version: 5.32.1 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.1 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.1 - **fixed** — Resolve children snapshot union pollution for typed invoke ###### Patch Changes - [#5516](https://github.com/statelyai/xstate/pull/5516) [`41c0a5a`](https://github.com/statelyai/xstate/commit/41c0a5a2878713bde019c61099f9486fae2c70f8) Thanks [@joshuaellis](https://github.com/joshuaellis)! - fix(core): resolve children snapshot union pollution for typed invoke ### 5.32.0 - Date: 2026-05-27 - Version: 5.32.0 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.32.0 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.32.0 - **added** — Export InspectedTransitionEvent from xstate ###### Minor Changes - [#5512](https://github.com/statelyai/xstate/pull/5512) [`063416d`](https://github.com/statelyai/xstate/commit/063416db859581b91fd661ae1a89b75a37fffa69) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Export `InspectedTransitionEvent` from `xstate`. ### 5.31.1 - Date: 2026-05-10 - Version: 5.31.1 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.31.1 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.31.1 - **fixed** — Fixed route transition guards so named guards registered with setup({ guards }) are resolved for route.guard ###### Patch Changes - [#5525](https://github.com/statelyai/xstate/pull/5525) [`f79ea13`](https://github.com/statelyai/xstate/commit/f79ea13febe44956d309811a64b9b79f4f3d9295) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Fixed route transition guards so named guards registered with `setup({ guards })` are resolved for `route.guard`. ```ts const machine = setup({ guards: { isReady: ({ context }) => context.ready } }).createMachine({ states: { review: { id: 'review', route: { guard: 'isReady' } } } }); ``` ### 5.31.0 - Date: 2026-04-27 - Version: 5.31.0 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.31.0 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.31.0 - **added** — Add mapState(snapshot, mapper) to map a snapshot to values based on active state(s) - **added** — Add maxIterations option to configure the maximum number of microsteps allowed before throwing an infinite loop error ###### Minor Changes - [#5429](https://github.com/statelyai/xstate/pull/5429) [`9d9c1fe`](https://github.com/statelyai/xstate/commit/9d9c1fe9df43936aa6ab43a4694645a6966ace12) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Add `mapState(snapshot, mapper)` to map a snapshot to values based on active state(s). ```ts import { mapState } from 'xstate'; const results = mapState(snapshot, { states: { loading: { map: () => 'Loading...' }, success: { map: (snap) => snap.context.data }, error: { map: (snap) => snap.context.error.message } } }); console.log(results); // E.g. if snapshot.value === 'loading', then: // [ // { stateNode: { key: 'loading' }, result: 'Loading...' } // ] ``` - [#5430](https://github.com/statelyai/xstate/pull/5430) [`e543599`](https://github.com/statelyai/xstate/commit/e5435993b2dee17c0808c2997665df696865aebd) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Add `maxIterations` option to configure the maximum number of microsteps allowed before throwing an infinite loop error. The default is `Infinity` (no limit) to avoid breaking existing machines. You can configure it when creating a machine: ```ts const machine = createMachine({ // ... machine config options: { maxIterations: 1000 // set a limit to enable infinite loop detection } }); ``` ### 5.30.0 - Date: 2026-03-28 - Version: 5.30.0 - Original notes: https://github.com/statelyai/xstate/releases/tag/xstate%405.30.0 - Permalink: https://whatsnew.fyi/product/xstate/releases/5.30.0 - **added** — Add a filterEvents option to xstate/graph traversal helpers and createTestModel(...) to control which events should be explored from each state ###### Minor Changes - [#5493](https://github.com/statelyai/xstate/pull/5493) [`871857d`](https://github.com/statelyai/xstate/commit/871857d730b7c333728da2b17bc36844697e8f88) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Add a `filterEvents` option to `xstate/graph` traversal helpers and `createTestModel(...)` to control which events should be explored from each state. This makes it possible to opt into enabled-only traversal for machine snapshots, such as when you only want to explore events that currently pass guards: ```ts import { createTestModel } from 'xstate/graph'; const model = createTestModel(machine); const paths = model.getSimplePaths({ filterEvents: (state, event) => state.can(event) }); ```