This package contains a minimal implementation of XState FSM for finite state machines with addition of extra features we need for our components.
and, or, not)Note: The core package is intentionally minimal. It doesn't include delayed transitions, interval scheduling, spawn helpers, or activities. Use actions/effects to perform side effects.
To better understand the state machines, we strongly recommend going though the xstate docs and videos. It'll give you the foundations you need.
Installation
npm i @zag-js/core
# or
yarn add @zag-js/core
Usage (machine):
import { createMachine } from "@zag-js/core"
const toggleMachine = createMachine({
id: "toggle",
initialState() {
return "inactive"
},
states: {
inactive: { on: { TOGGLE: "active" } },
active: { on: { TOGGLE: "inactive" } },
},
})
Usage (service via adapter):
import { createMachine } from "@zag-js/core"
import { VanillaMachine } from "@zag-js/vanilla"
const machine = createMachine({
initialState() {
return "inactive"
},
states: {
inactive: { on: { TOGGLE: "active" } },
active: { on: { TOGGLE: "inactive" } },
},
})
const service = new VanillaMachine(machine)
service.start()
service.subscribe(() => {
console.log(service.state.get())
})
service.send({ type: "TOGGLE" })
service.send({ type: "TOGGLE" })
service.stop()
import { createMachine } from "@zag-js/core"
const machine = createMachine({
initialState() {
return "dialog"
},
states: {
dialog: {
tags: ["overlay"],
initial: "closed",
states: {
closed: { on: { OPEN: { target: "dialog.open" } } },
open: { on: { CLOSE: { target: "dialog.closed" } } },
},
// parent-level transitions still work
on: { RESET: { target: "dialog.closed" } },
},
},
})
// service.state.matches("dialog.open") === true when nested state is active
#id targetsUse id on a state node when you want to target it explicitly from anywhere in the machine.
import { createMachine } from "@zag-js/core"
const machine = createMachine({
initialState() {
return "dialog"
},
states: {
dialog: {
initial: "open",
states: {
focused: {
id: "dialogFocused",
},
open: {
initial: "idle",
states: {
idle: {
on: {
CLOSE: { target: "#dialogFocused" },
},
},
},
},
},
},
},
})
Notes:
#id targets resolve by state node id (XState-style).createMachine(config, options)Creates a new finite state machine from the config.
| Argument | Type | Description |
|---|---|---|
config | object (see below) | The config object for creating the machine. |
options | object (see below) | The optional options object. |
Returns:
A machine definition object consumed by framework adapters (React, Solid, Svelte, Vue, Preact, Vanilla) to create a runtime service.
The machine config has this schema:
initialState (function) - returns the machine's initial state value.states (object) - mapping of state names to state configs. Supports nested states and initial for child states.on (object) - optional global transitions available from any state.context (function) - returns bindable context fields.computed (object) - derived values based on context, props, refs.entry / exit / effects - root-level actions/effects.implementations (object) - lookup tables for actions, guards, effects.on (object) - an object mapping event types (keys) to transitionsString syntax:
{ target: stateName }Object syntax:
target (string) - the state name to transition to.actions (Action | Action[]) - the action(s) to execute when this transition is taken.guard (Guard) - the condition (predicate function) to test. If it returns true, the transition will be taken.actions? (object) - a lookup object for your string actions.guards? (object) - a lookup object for your string guards specified as guard in the machine.activities? (object) - a lookup object for your string activities.delays? (object) - a lookup object for your string delays used in after and every config.The action function to execute while transitioning from one state to another. It takes the following arguments:
context (any) - the machine's current context.event (object) - the event that caused the action to be executed.