Multi-Page Tour

A driver instance lives within a single page: a full page load destroys it, and even in single-page apps the next route’s elements don’t exist while the current route is showing. The reliable pattern for tours that span navigation is to treat the tour as resumable: persist the step to resume at, let the app navigate normally, and start a fresh tour from the saved step on the next page.

Saving Progress Before Navigating

Define the tour in a function so every page can create it. On the step whose click navigates away, use advanceOnClick together with an onNextClick override: instead of moving to a step that lives on another page, save where to resume and tear the tour down. The link’s own click keeps working, so navigation happens naturally.

import { driver } from "driver.js";
import "driver.js/dist/driver.css";

const TOUR_KEY = "app-tour-step";

export function createTour() {
  let isNavigating = false;

  const driverObj = driver({
    steps: [
      {
        element: "#reports-nav",
        popover: { title: "Reports", description: "Your reports live here." },
      },
      {
        element: "#settings-link",
        // Clicking the highlighted link continues the tour and navigates.
        advanceOnClick: true,
        popover: {
          title: "Open Settings",
          description: "Click this link to continue on the settings page.",
          showButtons: ["close"],
          onNextClick: () => {
            // The next step lives on another page: save where to resume
            // and let the click's navigation happen naturally.
            isNavigating = true;
            localStorage.setItem(TOUR_KEY, "2");
            driverObj.destroy();
          },
        },
      },
      // These steps target elements on the settings page. waitForElement
      // absorbs the rendering delay after a client-side route change.
      {
        element: "#profile-form",
        waitForElement: 5000,
        popover: { title: "Your Profile", description: "Update your details here." },
      },
      {
        element: "#save-settings",
        popover: { title: "Save", description: "Don't forget to save your changes." },
      },
    ],
    onDestroyed: () => {
      // Finished or exited: clear the saved progress so the tour does not
      // come back on the next visit. Skipped for the hand-off destroy above.
      if (!isNavigating) {
        localStorage.removeItem(TOUR_KEY);
      }
    },
  });

  return driverObj;
}

Resuming on the Next Page

On every page load (or in your router’s mount hook), check for saved progress and resume from it:

const savedStep = localStorage.getItem(TOUR_KEY);
if (savedStep !== null) {
  createTour().drive(Number(savedStep));
}

drive() accepts the step index to start from, so the tour picks up exactly where it left off.

Notes