Skip to content

createMachine

Creates a state machine definition that can be interpreted into an actor.

Signature

function createMachine<
TStateValue extends string,
TEvent extends MachineEvent,
TContextSchema extends Schema.Schema.Any,
R = never,
E = never,
>(config: MachineConfig): MachineDefinition

Type Parameters

ParameterDescription
TStateValueUnion of state value strings (e.g., "idle" | "loading" | "done")
TEventUnion of event types extending MachineEvent
TContextSchemaThe Effect Schema type (use typeof YourSchema)
REffect requirements (service dependencies)
EEffect error type

Config Properties

PropertyTypeRequiredDescription
idstringYesUnique identifier for the machine
initialTStateValueYesInitial state value
contextSchema.SchemaYesEffect Schema for context validation and serialization
initialContextTContextYesInitial context value
statesRecord<TStateValue, StateNodeConfig>YesState definitions

StateNodeConfig

Each state can have the following properties:

PropertyTypeDescription
entryAction[]Actions to run when entering this state
exitAction[]Actions to run when leaving this state
onRecord<EventTag, TransitionConfig>Event handlers
activitiesActivityConfig[]Long-running effects active in this state
invokeInvokeConfigOne-shot async operation
afterDelayedTransitionConfigDelayed/timed transitions

TransitionConfig

PropertyTypeDescription
targetTStateValueTarget state (optional, stay in current if omitted)
guardGuard<TContext, TEvent>Condition for transition
actionsAction[]Actions to run during transition

Returns

Returns a MachineDefinition object with:

PropertyTypeDescription
_tag"MachineDefinition"Type discriminator
idstringMachine identifier
configMachineConfigThe original config
initialSnapshotMachineSnapshotInitial state snapshot
contextSchemaSchema.SchemaThe context schema

Examples

Basic Machine

import { createMachine, assign } from "effstate";
import { Data, Schema } from "effect";
// Define events
class Increment extends Data.TaggedClass("INCREMENT")<{}> {}
class Reset extends Data.TaggedClass("RESET")<{}> {}
type CounterEvent = Increment | Reset;
// Define context schema
const ContextSchema = Schema.Struct({
count: Schema.Number,
});
// Create machine with explicit type parameters
const counterMachine = createMachine<
"idle" | "active",
CounterEvent,
typeof ContextSchema
>({
id: "counter",
initial: "idle",
context: ContextSchema,
initialContext: { count: 0 },
states: {
idle: {
on: {
INCREMENT: {
target: "active",
actions: [assign(({ context }) => ({ count: context.count + 1 }))],
},
},
},
active: {
on: {
INCREMENT: {
actions: [assign(({ context }) => ({ count: context.count + 1 }))],
},
RESET: {
target: "idle",
actions: [assign({ count: 0 })],
},
},
},
},
});

With Entry/Exit Actions

const machine = createMachine({
id: "door",
initial: "closed",
context: ContextSchema,
initialContext: { openCount: 0 },
states: {
closed: {
entry: [effect(() => Effect.log("Door closed"))],
on: {
OPEN: { target: "open" },
},
},
open: {
entry: [
effect(() => Effect.log("Door opened")),
assign(({ context }) => ({ openCount: context.openCount + 1 })),
],
exit: [effect(() => Effect.log("Leaving open state"))],
on: {
CLOSE: { target: "closed" },
},
},
},
});

With Delayed Transitions

Delayed transitions automatically fire after a specified delay. By default, delays are auto-cancelled when exiting the state (preventing the “spam button” bug).

import { Duration, Effect } from "effect";
const machine = createMachine({
id: "timeout",
initial: "idle",
context: ContextSchema,
initialContext: {},
states: {
idle: {
on: {
START: { target: "running" },
},
},
running: {
// Simple duration-based delay (auto-cancelled on state exit)
after: {
delay: Duration.seconds(5),
transition: { target: "timedOut" },
},
on: {
COMPLETE: { target: "done" },
},
},
timedOut: {},
done: {},
},
});

Delay formats:

// Numeric shorthand (milliseconds)
after: {
5000: { target: "timedOut" }
}
// Duration-based (recommended)
after: {
delay: Duration.seconds(5),
transition: { target: "timedOut" }
}
// Persistent delay (survives state exits - for global timeouts)
after: {
delay: Duration.minutes(30),
transition: { target: "sessionTimeout" },
persistent: true
}
// Effect-based delay (full control with interruption handlers)
after: {
delay: Effect.sleep(Duration.seconds(5)).pipe(
Effect.onInterrupt(() => telemetry.trackCancellation())
),
transition: { target: "timedOut" }
}
// Persistent + Effect (both features combined)
after: {
delay: Effect.gen(function* () {
yield* Effect.sleep(Duration.minutes(1));
yield* metrics.recordHeartbeat();
}),
transition: { target: "timeout" },
persistent: true
}
OptionDescription
delayDuration.DurationInput or Effect<void, never, R>
transitionTarget state and optional actions
persistentIf true, delay survives state exits (only cancelled on actor.stop())

With Activities

const machine = createMachine({
id: "polling",
initial: "idle",
context: ContextSchema,
initialContext: { data: null },
states: {
idle: {
on: { START: { target: "polling" } },
},
polling: {
activities: [
{
id: "pollData",
src: ({ send }) =>
Effect.gen(function* () {
while (true) {
yield* Effect.sleep("5 seconds");
const data = yield* fetchData();
send(new DataReceived({ data }));
}
}),
},
],
on: {
DATA_RECEIVED: {
actions: [assign(({ event }) => ({ data: event.data }))],
},
STOP: { target: "idle" },
},
},
},
});

With Invoke

Use the invoke() helper for proper type inference:

import { createMachine, assign, invoke } from "effstate";
const machine = createMachine({
id: "userLoader",
initial: "idle",
context: ContextSchema,
initialContext: { user: null, error: null },
states: {
idle: {
on: { LOAD: { target: "loading" } },
},
loading: {
invoke: invoke({
id: "loadUser",
src: ({ context }) => fetchUser(context.userId),
onSuccess: {
target: "ready",
actions: [assign(({ event }) => ({ user: event.output }))],
},
onFailure: {
target: "error",
actions: [assign(({ event }) => ({ error: event.error }))],
},
}),
},
ready: {},
error: {},
},
});

See Also

  • interpret - Create an actor from a machine definition
  • Actions - Available action creators
  • Guards - Guard helpers