Guides & orchestration
A guide orders steps. Each entry can carry a route, a condition, hooks and overrides:
export const dealEditorTour = defineGuide({ id: "deal-editor", version: 2, steps: [ ...onPage("/groups", [createGroupStep, viewToggleStep]), ...onPage("/deals/new", [ dealerStep, { step: bidsStep, beforeEnter: () => editorUi.openTab("auction"), onMissing: "end" }, { step: studentStep, when: () => flags.students }, ]), ],});onPage and routes
onPage(route, steps) sets route on each entry and keeps its hooks and overrides. A string route is compared to the exact pathname (no query, no hash); a function receives the pathname and returns a boolean. When the next step's route does not match, the manager navigates through the router you gave to GuideRoot, waits for the pathname to match, then waits for the target. Routes are declarative and idempotent: going back a step that is on another page navigates back.
when is evaluated once, at start. The run's total stays stable while it runs: the counter never jumps from "3 of 5" to "3 of 4".
Lifecycle, two levels
A transition runs hooks at two levels: the entry (declared in the guide, always there) and the local lifecycle (registered by the anchored component, only while it is mounted). The order is fixed:
beforeLeaveof the current step: entry, then local.- Route: navigate and wait for the pathname.
beforeEnterof the entry: open the tab, expand the panel, whatever makes the target exist.- Wait for a target (and the content, when the anchor provides one).
beforeEnterlocal: the component is mounted now, it can prepare its own state.- Scroll into view (modal), commit, record.
afterLeaveof the step left (local, then entry), thenafterEnter(entry, then local).
Every hook receives a context with the direction (forward, backward, jump), from and to, an AbortSignal, navigate, waitFor and the manager. The latest request wins: clicking Next twice aborts the first transition (its signal fires) and runs the second. Each hook is bounded by hookTimeout (10 s by default).