Programmatic control guide
GlowTour.js provides a complete programmatic API for controlling tours, observing state changes, and sequencing complex workflows.
Tour instance
Section titled “Tour instance”Every adapter’s createGlowTour() function returns a tour controller. Keep this instance alive for your app’s lifetime; it holds state, manages workflows, and dispatches events.
import { createGlowTour } from "@glowhop/react-tour";
const tour = createGlowTour();// Reuse the same instance across your appTour state
Section titled “Tour state”Access the current tour state and subscribe to changes:
Reading state
Section titled “Reading state”const state = tour.state.get();
console.log(state.status); // "idle" | "starting" | "transitioning" | "active" | "finished" | "cancelled" | "error" | "disposed"console.log(state.currentStep); // Current step info (or null if not active)console.log(state.error); // Error if status === "error"State includes:
name- Name of the running workflowtotalSteps- Total number of steps in the workflowcurrentStepIndex- Index of the active step (0-based), or -1 if nonestatus- Current tour statecurrentStep- Current step datadirection- Direction of the last navigation (“advance” or “previous”)canAdvance- Whether advancing is allowedcanPrevious- Whether going back is allowedcanCancel- Whether cancelling is allowedisFirstStep- Whether the tour is on the first stepisLastStep- Whether the tour is on the last steperror- Error if the tour failed
Subscribing to changes
Section titled “Subscribing to changes”const unsubscribe = tour.state.subscribe((newState) => { console.log("Tour state changed:", newState); if (newState.status === "finished") { console.log("Tour finished!"); }});
// Call unsubscribe() to stop listeningunsubscribe();Running tours
Section titled “Running tours”Basic run
Section titled “Basic run”const workflow = tour.create("intro").step({ id: "step-1", /* ... */ }).build();await tour.run(workflow);console.log("Tour completed");The run() method is async and resolves when the tour completes, is cancelled, or errors.
Navigation commands
Section titled “Navigation commands”While a tour is active, control it with these methods:
// Move to the next stepawait tour.advance();
// Go to the previous stepawait tour.previous();
// Jump to a specific step by indexawait tour.goToStep(2);
// Cancel and end the tourawait tour.cancel();
// Clean up and release resourcestour.dispose();Lifecycle callbacks
Section titled “Lifecycle callbacks”React to tour events at the workflow level:
const workflow = tour .create("my-tour", { onStart(context) { console.log("Tour started on step:", context.step?.initialProps.title); }, onCancel(context) { console.log("Tour cancelled by user at step:", context.step?.initialProps.title); }, onFinish(context) { console.log("Tour completed all steps, last step:", context.step?.initialProps.title); }, }) .step({ id: "step1", /* ... */ }) .build();Transition callbacks
Section titled “Transition callbacks”React to step transitions. These are builder methods chained after a .step() call, not options
inside it - they attach to the step that precedes them:
const workflow = tour .create("transitions") .step({ id: "step1-2", target: "#step1", title: "First", content: "Step 1", }) .beforeAdvance(async (context) => { console.log("About to advance from step 1"); // Perform async work, e.g., save user progress await saveProgress(); }) .step({ id: "step2", target: "#step2", title: "Second", content: "Step 2", }) .beforePrevious(async (context) => { console.log("About to go back to step 1"); }) .step({ id: "step3", target: "#step3", title: "Third", content: "Step 3", }) .beforeCancel(async (context) => { console.log("About to cancel the tour"); }) .build();.beforeAdvance(), .beforePrevious(), and .beforeCancel() can be async and will pause the transition until they resolve.
Step actions
Section titled “Step actions”Sequence work between steps using .do(), .wait(), and other action methods:
const workflow = tour .create("with-actions") .step({ id: "field", target: "#field", title: "Enter data", content: "Type something in this field.", }) .do(async () => { console.log("User finished step 1"); }) .wait(1000) // Wait 1 second .step({ id: "submit", target: "#submit", title: "Submit", content: "Click the submit button.", }) .waitUntil(() => { // Wait until form is submitted return document.querySelector("form")?.dataset.submitted === "true"; }) .step({ id: "success", target: "#success", title: "Done!", content: "Your form was submitted.", }) .build();Available actions:
.do(fn)- Execute a function (can be async).wait(ms)- Wait for a duration in milliseconds.waitUntil(fn, options)- Wait until a condition is true (default: checks every 16ms, 3000ms timeout).waitUntilElement(selector, options)- Wait until an element enters the DOM.clickTarget()- Click the current step’s target element.focusTarget()- Focus the current step’s target element
Composing workflows
Section titled “Composing workflows”.append(workflow) splices an already-built workflow’s steps into the one you are building, so you can
define reusable fragments once and reuse them across tours:
const profileSteps = tour .create("profile-fragment") .step({ id: "profile", target: "#profile", title: "Your profile", content: "Complete it to continue." }) .build();
const workflow = tour .create("onboarding") .step({ id: "welcome", target: "#welcome", title: "Welcome", content: "Let's get started!" }) .append(profileSteps) .step({ id: "dashboard", target: "#dashboard", title: "You're ready!", content: "Explore your dashboard." }) .build();Target events
Section titled “Target events”React to DOM events on the current target:
const workflow = tour .create("events") .step({ id: "button", target: "#button", title: "Click me", content: "This button triggers an action.", }) .onTargetEvent("click", (event, context) => { console.log("Target was clicked during this step"); }) .step({ id: "next", target: "#next", title: "Next", content: "Continue the tour.", }) .build();The event handler receives the native DOM event and the step context.
Pass an array to bind the same handler to several events at once:
.onTargetEvent(["focus", "blur"], (event, context) => { console.log("Target received:", event.type);})Error handling
Section titled “Error handling”Handle subscriber errors that don’t crash the tour:
const tour = createGlowTour({ onSubscriberError(error) { console.error("A subscriber threw an error:", error); // Log it, report it, but the tour continues },});State subscriber functions or step callback functions that throw are caught, normalized to Error, and reported to onSubscriberError. They do not fail the tour transition.
A fatal error from the rendering layer (e.g., the popover component throws) will reject the command and set the tour state to status === "error" with the error details.
Example: complex tour
Section titled “Example: complex tour”Here’s a tour that combines multiple features:
// `createGlowTour` only takes controller-level options; lifecycle hooks belong to the workflow.const tour = createGlowTour({ onSubscriberError(error) { logger.error("Tour error", error); },});
const workflow = tour .create("onboarding", { onStart(context) { analytics.track("tour_started"); }, onCancel(context) { analytics.track("tour_cancelled"); }, onFinish(context) { analytics.track("tour_completed"); }, }) .step({ id: "welcome-2", target: "#welcome", title: "Welcome", content: "Let's get started!", }) .beforeAdvance(async () => { await api.logEvent("welcome_seen"); }) .wait(500) .step({ id: "profile-2", target: "#profile", title: "Your profile", content: "Complete your profile to unlock all features.", }) .waitUntil(() => { return document.querySelector("form")?.dataset.valid === "true"; }) .do(async () => { await api.submitProfile(); }) .step({ id: "dashboard-2", target: "#dashboard", title: "You're ready!", content: "Explore your dashboard.", }) .beforeCancel(async (context) => { // The transition context carries the step's props and its resolved target element. await api.logEvent("cancelled_on", { step: context.title }); }) .build();
// Run the tourawait tour.run(workflow);For the full workflow/step-building API and every option’s default value, see the Builder reference; for the controller API (createGlowTour, tour.run, tour.state, …), see the Tour reference.
