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:
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' },
},
});Ensure you are using the latest version of TypeScript (version 5.0 or higher). See the docs on TypeScript usage with XState for more information.
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 actionparams - Types for
guards, including guardparams - Types for
actors
Extending setup
Since XState version 5.24.0
Use setup.extend(...) to create a new setup from an existing setup. Extensions
can add actions, guards, and delays; all base and extended
implementations are available to machines created from the extended setup.
import { and, not, setup } from 'xstate';
const baseSetup = setup({
guards: {
isAuthenticated: () => true,
hasPermission: () => false,
},
});
const appSetup = baseSetup.extend({
guards: {
isUnauthenticated: not('isAuthenticated'),
canAccess: and(['isAuthenticated', 'hasPermission']),
},
actions: {
trackAccess: () => {
console.log('Access checked');
},
},
delays: {
shortDelay: 250,
},
});
const machine = appSetup.createMachine({
initial: 'checking',
states: {
checking: {
after: {
shortDelay: {
guard: 'canAccess',
actions: 'trackAccess',
target: 'allowed',
},
},
},
allowed: {},
},
});You can chain setup.extend(...) calls when different modules need to add their
own implementations:
const browserSetup = baseSetup.extend({
actions: {
readFromStorage: () => {
// ...
},
},
});
const authenticatedBrowserSetup = browserSetup.extend({
guards: {
canReadStorage: and(['isAuthenticated', 'hasPermission']),
},
});Migrating from createMachine
Migrating from bare createMachine({ ... }) to setup({ ... }).createMachine({ ... }) to create a machine is simple.
- Import
setupinstead ofcreateMachinefrom'xstate' - Move
typesfromcreateMachine(...)tosetup(...) - Move action, actor, guard, etc. sources from the 2nd argument of
createMachine(config, sources)tosetup({ ... })
import {
// createMachine
setup
} from 'xstate';
const machine =
setup({
types: { ... },
actions: { ... },
guards: { ... }
})
.createMachine({
// types: { ... }
}, /* { actions, guards, ... } */);