setup() function is the recommended way to create state machines in XState v5, as it provides excellent TypeScript type inference and allows you to define reusable implementations.
Signature
function setup<
TContext extends MachineContext,
TEvent extends AnyEventObject,
TActors extends Record<string, UnknownActorLogic>,
TChildrenMap extends Record<string, string>,
TActions extends Record<string, ParameterizedObject['params'] | undefined>,
TGuards extends Record<string, ParameterizedObject['params'] | undefined>,
TDelay extends string,
TTag extends string,
TInput,
TOutput extends NonReducibleUnknown,
TEmitted extends EventObject,
TMeta extends MetaObject
>({
types,
actors,
actions,
guards,
delays
}): SetupReturn<...>
Parameters
Configuration object for the machine setup.
Show properties
Show properties
Type definitions for TypeScript inference. This is a compile-time only property.
types: {} as {
context: { count: number };
events: { type: 'INCREMENT' } | { type: 'DECREMENT' };
input: { initialCount: number };
output: number;
}
A map of actor logic creators that can be invoked or spawned.
actors: {
fetchUser: fromPromise(async ({ input }) => {
const response = await fetch(`/api/users/${input.userId}`);
return response.json();
})
}
A map of action implementations that can be referenced by name.
actions: {
logMessage: ({ context, event }) => {
console.log(context.message, event);
}
}
A map of guard (condition) implementations that can be referenced by name.
guards: {
isPositive: ({ context }) => context.count > 0,
isEven: ({ context }) => context.count % 2 === 0
}
A map of delay values or functions for delayed transitions and actions.
delays: {
SHORT: 1000,
LONG: ({ context }) => context.timeout
}
Returns
An object with methods for creating machines and type-safe helpers.
Show methods
Show methods
Creates a state machine with the configured types and implementations.
createMachine(config: MachineConfig): StateMachine
Returns a new setup with additional actions, guards, and delays merged with existing ones.
extend({ actions?, guards?, delays? }): SetupReturn
Type-safe version of the assign action creator.
Type-safe version of the sendTo action creator.
Type-safe version of the raise action creator.
Type-safe version of the log action creator.
Type-safe version of the cancel action creator.
Type-safe version of the stopChild action creator.
Type-safe version of the enqueueActions action creator.
Type-safe version of the emit action creator.
Type-safe version of the spawnChild action creator.
Creates a type-safe state config that can be reused in multiple machines.
Creates a type-safe action implementation.
Examples
Basic setup with types
import { setup, createActor } from 'xstate';
const counterMachine = setup({
types: {} as {
context: { count: number };
events: { type: 'INCREMENT' } | { type: 'DECREMENT' };
}
}).createMachine({
initial: 'active',
context: { count: 0 },
states: {
active: {
on: {
INCREMENT: {
actions: assign({
count: ({ context }) => context.count + 1
})
},
DECREMENT: {
actions: assign({
count: ({ context }) => context.count - 1
})
}
}
}
}
});
Setup with actions and guards
import { setup } from 'xstate';
const feedbackMachine = setup({
types: {} as {
context: { feedback: string; retries: number };
events:
| { type: 'SUBMIT' }
| { type: 'RETRY' }
| { type: 'CANCEL' };
},
actions: {
logSubmission: ({ context }) => {
console.log('Submitting feedback:', context.feedback);
},
incrementRetries: assign({
retries: ({ context }) => context.retries + 1
}),
clearRetries: assign({
retries: 0
})
},
guards: {
canRetry: ({ context }) => context.retries < 3,
hasValidFeedback: ({ context }) => context.feedback.length > 0
}
}).createMachine({
initial: 'editing',
context: { feedback: '', retries: 0 },
states: {
editing: {
on: {
SUBMIT: {
guard: 'hasValidFeedback',
target: 'submitting'
}
}
},
submitting: {
entry: 'logSubmission',
on: {
SUCCESS: {
target: 'success',
actions: 'clearRetries'
},
ERROR: {
guard: 'canRetry',
target: 'editing',
actions: 'incrementRetries'
}
}
},
success: {
type: 'final'
}
}
});
Setup with actors
import { setup, fromPromise } from 'xstate';
const userMachine = setup({
types: {} as {
context: { user: User | null };
events: { type: 'FETCH'; userId: string };
},
actors: {
fetchUser: fromPromise(async ({ input }: { input: { userId: string } }) => {
const response = await fetch(`/api/users/${input.userId}`);
return response.json();
})
}
}).createMachine({
initial: 'idle',
context: { user: null },
states: {
idle: {
on: {
FETCH: 'loading'
}
},
loading: {
invoke: {
src: 'fetchUser',
input: ({ event }) => ({ userId: event.userId }),
onDone: {
target: 'success',
actions: assign({
user: ({ event }) => event.output
})
},
onError: 'failure'
}
},
success: {},
failure: {}
}
});
Setup with delays
import { setup } from 'xstate';
const timeoutMachine = setup({
types: {} as {
context: { timeout: number };
events: { type: 'START' } | { type: 'RESET' };
},
delays: {
SHORT: 1000,
LONG: 5000,
DYNAMIC: ({ context }) => context.timeout
}
}).createMachine({
initial: 'idle',
context: { timeout: 3000 },
states: {
idle: {
on: {
START: 'waiting'
}
},
waiting: {
after: {
DYNAMIC: 'done'
},
on: {
RESET: 'idle'
}
},
done: {
type: 'final'
}
}
});
Using extend
import { setup } from 'xstate';
const baseSetup = setup({
types: {} as {
context: { count: number };
events: { type: 'INCREMENT' };
},
actions: {
logCount: ({ context }) => {
console.log(context.count);
}
}
});
const extendedSetup = baseSetup.extend({
actions: {
logDouble: ({ context }) => {
console.log(context.count * 2);
}
}
});
const machine = extendedSetup.createMachine({
initial: 'active',
context: { count: 0 },
states: {
active: {
entry: ['logCount', 'logDouble']
}
}
});
Using input and output types
import { setup } from 'xstate';
const calculatorMachine = setup({
types: {} as {
context: { total: number };
input: { initialValue: number };
output: number;
events: { type: 'ADD'; value: number };
}
}).createMachine({
initial: 'calculating',
context: ({ input }) => ({
total: input.initialValue
}),
states: {
calculating: {
on: {
ADD: {
actions: assign({
total: ({ context, event }) => context.total + event.value
})
},
FINISH: 'done'
}
},
done: {
type: 'final'
}
},
output: ({ context }) => context.total
});
const actor = createActor(calculatorMachine, {
input: { initialValue: 10 }
});
actor.start();
actor.send({ type: 'ADD', value: 5 });
Notes
- The
typesproperty is used solely for TypeScript type inference and has no runtime behavior - Actions, guards, actors, and delays defined in setup can be referenced by their string keys in the machine configuration
- Using
setup()is recommended over passing implementations as a second argument tocreateMachine() - The
extend()method allows for composition and reuse of configurations
See also
- createMachine() - Create a state machine
- assign() - Update context
- Actions guide - Working with actions
- Guards guide - Working with guards