Quickstart
This page builds a typed linear journey, starts it, observes it, and completes it.
Install
Section titled “Install”pnpm add @rxova/journey-coreDefine a flow
Section titled “Define a flow”import { createLinearJourney } from "@rxova/journey-core";
type CheckoutStepId = "account" | "shipping" | "review";type CheckoutContext = { email: string; country: string };type CheckoutTerminationPayloads = { complete: { orderId: string }; terminate: { reason: "cancelled" };};
const checkout = createLinearJourney<CheckoutStepId, CheckoutContext, CheckoutTerminationPayloads>({ steps: [ { id: "account", metadata: { title: "Account" } }, { id: "shipping", metadata: { title: "Shipping" } }, { id: "review", metadata: { title: "Review" } } ] as const, context: { email: "", country: "" }});String steps are valid shorthand when a step has no metadata or hooks:
const compact = createLinearJourney({ steps: ["account", "shipping", "review"] as const, context: { email: "", country: "" }});Drive it
Section titled “Drive it”Creating a machine starts it — autoStart defaults to true. The initial step is committed
synchronously, so getSnapshot().currentStep is readable right away, but an async initial onEnter
may still be in flight.
Pass { autoStart: false } when you need the machine to stay idle: that is the only way to attach a
subscriber before the journey’s first stepEnter, since starting emits it before the factory
returns. Then call checkout.controls.start() when you are ready.
function waitUntilSettled(machine: typeof checkout): Promise<void> { if (!machine.getSnapshot().transition.pending) return Promise.resolve();
return new Promise((resolve) => { const stop = machine.subscriptions.subscribe(() => { if (!machine.getSnapshot().transition.pending) { stop(); resolve(); } }); });}
await waitUntilSettled(checkout);
checkout.context.update((context) => ({ ...context, email: "ada@example.com"}));
const result = await checkout.navigate.goToNextStep();
if (!result.ok) { console.error(result.reason);}Every navigation method resolves to a NavigationResult:
type NavigationResult<StepId extends string> = | { ok: true; from: StepId | null; to: StepId } | { ok: false; reason: NavigationFailureReason; error?: unknown };Use snapshot.transition.pending as the default flag for disabling UI controls while navigation
work or lifecycle effects settle.
Read the snapshot
Section titled “Read the snapshot”const snapshot = checkout.getSnapshot();
snapshot.type; // "linear"snapshot.status; // "running"snapshot.currentStep?.id; // "shipping"snapshot.currentStep?.metadata.title; // "Shipping"snapshot.currentStep?.index; // 1snapshot.history.timeline; // ["account", "shipping"]snapshot.transition.pending; // falsesnapshot.steps.totalSteps; // 3Subscribe
Section titled “Subscribe”Subscriptions are grouped under machine.subscriptions.
const stopStepSubscription = checkout.subscriptions.subscribe(() => renderStep(checkout.getSnapshot().currentStep?.id));
const stopErrors = checkout.subscriptions.subscribeEvent("error", ({ error, phase }) => { reportError(error, phase);});Selectors run when their selected value changes. subscribeEvent listens to one named event and
returns an unsubscribe function.
Complete and restart
Section titled “Complete and restart”await checkout.navigate.goToNextStep();checkout.controls.complete({ orderId: "order-42" });
checkout.getSnapshot().machine.outcome;// { type: "completed", payload: { orderId: "order-42" } }
checkout.controls.restart();restart() is accepted only from completed or terminated. It restores the initial context,
clears history and outcome, and enters the initial step again.
Reaching review did not complete the journey automatically. The last screen is a position;
controls.complete() records the separate product outcome. The optional third factory generic
above makes both terminal control payloads and snapshot.machine.outcome type-safe.
Call checkout.dispose() when the runtime is no longer needed.
Where to next
Section titled “Where to next”- Linear journeys covers every linear navigation rule.
- Graph journeys adds typed events, guards, and branching.
- Snapshot documents the complete read model.
- Machine API lists every method and result.