Skip to content

chart()

The chart() function creates a statechart from a definition object.

function chart<TContext, TEvent>(
definition: ChartDefinition<TContext, TEvent>
): Chart<TContext, TEvent>

The chart definition object with these properties:

PropertyTypeRequiredDescription
idstringNoIdentifier for the chart
contextTContextYesInitial context data
initialstringYesInitial state name
statesRecord<string, StateNode>YesState definitions

Returns a Chart object with:

Property/MethodTypeDescription
definitionChartDefinitionThe chart definition
start(initialContext?)ChartInstanceCreate a running instance

Each state in states can have:

interface StateNode<TContext, TEvent> {
entry?: Actions; // Run when entering state
exit?: Actions; // Run when exiting state
on?: Record<string, Transition>; // Event handlers
after?: Record<number, Transition>; // Delayed transitions (ms)
invoke?: InvokeFn; // Async invocation
onDone?: Transition; // Handle invoke success
onError?: Transition; // Handle invoke error
initial?: string; // Initial child state
states?: Record<string, StateNode>; // Nested states
final?: boolean; // Mark as final state
}

Transitions can be defined in several ways:

// String shorthand - just target state
on: { CLICK: "active" }
// Object with target
on: { CLICK: { target: "active" } }
// With guard
on: {
CLICK: {
target: "active",
guard: (ctx, event) => ctx.enabled,
}
}
// With action
on: {
CLICK: {
target: "active",
action: (ctx, event) => ({ clicks: ctx.clicks + 1 }),
}
}
// Multiple transitions (evaluated in order)
on: {
CLICK: [
{ target: "disabled", guard: (ctx) => ctx.clicks >= 10 },
{ target: "active" }
]
}

Actions receive context and event, and optionally return context updates:

const machine = chart({
context: { count: 0 },
initial: "idle",
states: {
idle: {
entry: (ctx) => console.log("Entered idle"),
exit: (ctx) => console.log("Exiting idle"),
on: {
INCREMENT: {
action: (ctx, event) => ({ count: ctx.count + 1 }),
},
},
},
},
});
import { chart } from "statecharts.sh";
const loginMachine = chart({
id: "login",
context: {
attempts: 0,
error: null as string | null,
},
initial: "idle",
states: {
idle: {
on: { SUBMIT: "loading" },
},
loading: {
invoke: async (ctx) => {
const response = await fetch("/api/login");
if (!response.ok) throw new Error("Failed");
return response.json();
},
onDone: { target: "success" },
onError: {
target: "idle",
action: (ctx, event) => ({
attempts: ctx.attempts + 1,
error: event.error.message,
}),
},
},
success: {
final: true,
},
},
});

To export a chart to SCJSON format (JSON translation of SCXML), use the exportSCJSON function from the /schema subpath:

import { chart } from "statecharts.sh";
import { exportSCJSON } from "statecharts.sh/schema";
const machine = chart({
id: "toggle",
context: {},
initial: "off",
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
},
});
const scjson = exportSCJSON(machine.definition);
// Returns SCJSONDocument compatible with SCXML tooling