Skip to main content
Configures and creates a type-safe state machine builder with predefined implementations for actions, guards, actors, and delays. The 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

config
object
required
Configuration object for the machine setup.

Returns

setupReturn
SetupReturn
An object with methods for creating machines and type-safe helpers.

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 types property 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 to createMachine()
  • The extend() method allows for composition and reuse of configurations

See also