use-machine
Finite state machine with typed states, events, and targets.
| Package | @vizejs/composable/use-machine |
| Own the source | vize lib pull composable:use-machine |
| Runtime exports | useMachine |
| Gzip budget | 1536 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useMachine } from "@vizejs/composable/use-machine";
Try it
Walk through a guarded checkout
Model a checkout as named steps so each screen only offers the actions that are valid there.
What to observe: The cart starts empty and only offers Add item. Add one item to unlock Go to shipping, then continue to Review order and Place order. Back returns one step; Start a new order empties the cart.
nextEvents lists the events the current state accepts and whose guards pass, so the buttons follow the machine. add is a self-transition whose action returns the next context. send() returns false for an event the state does not accept.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { useMachine } from "@vizejs/composable/use-machine";
type Step = "cart" | "shipping" | "review" | "placed";
type Action = "add" | "checkout" | "back" | "ship" | "place" | "restart";
const { state, context, nextEvents, send, can } = useMachine<Step, Action, { items: number }>({
initial: "cart",
context: { items: 0 },
states: {
cart: {
on: {
add: { target: "cart", action: (order) => ({ items: order.items + 1 }) },
checkout: { target: "shipping", guard: (order) => order.items > 0 },
},
},
shipping: { on: { back: "cart", ship: "review" } },
review: { on: { back: "shipping", place: "placed" } },
placed: { on: { restart: { target: "cart", action: () => ({ items: 0 }) } } },
},
});
const labels: Record<Action, string> = {
add: "Add item",
checkout: "Go to shipping",
back: "Back",
ship: "Review order",
place: "Place order",
restart: "Start a new order",
};
</script>
<template>
<div class="composable-example">
<p>Walk through a checkout that only moves forward when each step is ready.</p>
<output aria-live="polite">Step: {{ state }} · Items: {{ context.items }}</output>
<div class="example-actions">
<button v-for="event in nextEvents" :key="event" type="button" @click="() => send(event)">
{{ labels[event] }}
</button>
</div>
<p v-if="state === 'cart' && !can('checkout')">Add an item before checking out.</p>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useMachine |
state | experimental | safe | stable | none | web, server, worker, native, desktop, terminal | — | — |
API
useMachine
Finite state machine with typed states, events, and targets. State names are inferred from the keys of states, event names from the keys of every on map, and every target (and initial) must name a declared state, so typos fail to compile. Transitions may carry guards (first passing guard wins) and actions that return the next context; exit runs on the old state before entry runs on the new one, and a self-transition runs both. Synchronous and free of globals: identical on server and client, nothing to dispose.
function useMachine<State extends string, Event extends string = never, Context = undefined>( config: MachineConfig<State, Event, Context>, ): Machine<State, Event, Context>
const fetcher = useMachine({
initial: "idle",
context: { retries: 0 },
states: {
idle: { on: { FETCH: "loading" } },
loading: { on: { RESOLVE: "success", REJECT: "failure" } },
failure: {
on: {
RETRY: { target: "loading", guard: (c) => c.retries < 3, action: (c) => ({ retries: c.retries + 1 }) },
},
},
success: {},
},
});
fetcher.send("FETCH");
Types
MachineTransition
Guarded, effectful transition to target.
| Member | Type | Description |
|---|---|---|
target |
State |
State entered when the transition is taken. |
guard? |
(context: Context) => boolean |
Take the transition only when this returns true. |
action? |
(context: Context) => Context |
Produce the next context while transitioning. |
MachineStateNode
One state of a MachineConfig.
| Member | Type | Description |
|---|---|---|
on? |
{ readonly [Name in Event]?: | State | MachineTransition<State, Context> | readonly MachineTransition<State, Context>[]; } |
Transitions keyed by event: a target state name, one transition object, or a list of guarded transitions where the first passing guard wins. |
entry? |
(context: Context) => void |
Called with the context after the machine enters this state. |
exit? |
(context: Context) => void |
Called with the context before the machine leaves this state. |
MachineConfig
Definition consumed by useMachine.
| Member | Type | Description |
|---|---|---|
initial |
NoInfer<State> |
State the machine starts (and resets) in. |
context |
Context |
Initial extended state. |
states |
{ readonly [Name in State]: MachineStateNode<NoInfer<State>, Event, Context> } |
Every state, keyed by name. |
Machine
Reactive machine returned by useMachine.
| Member | Type | Description |
|---|---|---|
state |
Readonly<ShallowRef<State>> |
Current state name. |
context |
Readonly<ShallowRef<Context>> |
Current extended state. |
nextEvents |
ComputedRef<Event[]> |
Events that would cause a transition from the current state right now. |
send |
(event: Event) => boolean |
Deliver an event. |
can |
(event: Event) => boolean |
Whether event would cause a transition right now (guards included). |
matches |
(...states: readonly State[]) => boolean |
Whether the current state is one of states. |
reset |
() => void |
Return to the initial state and context without running hooks. |