Skip to content

Product Tours

Product tours are multi-step walkthroughs that guide users through a feature or workflow. Each step highlights a specific element on the page with a tooltip-style callout containing a title, body text, optional media, and navigation controls.

Overview

A tour consists of:

  • Steps -- Ordered sequence of targeted callouts, each attached to a DOM element
  • Start page -- Optional welcome screen shown before step 1
  • Finish page -- Optional completion screen shown after the last step
  • Progress indicator -- Visual progress bar or step counter
  • Navigation -- Next, Previous, and Skip controls with optional keyboard shortcuts

Creating a Tour

Via the Admin Panel

  1. Navigate to Content > Tours and click Create Tour.
  2. Enter a tour name and optional description.
  3. Click Add Step to open the visual element selector.
  4. Click on the element you want to highlight. The admin overlay captures the CSS selector.
  5. Write the step title and body content.
  6. Repeat for each step.
  7. Configure start/finish pages, trigger rules, and audience targeting.
  8. Click Publish.

Via the API

bash
POST /v1/admin/tours
Content-Type: application/json

{
  "title": "Dashboard Onboarding",
  "description": "Introduces new users to the main dashboard",
  "steps": [
    {
      "step_id": "welcome-nav",
      "order": 0,
      "title": "Navigation Menu",
      "body": "<p>Use the sidebar to navigate between sections.</p>",
      "element_target": { "data_attribute": "nav-menu" },
      "position": "right",
      "optional": false
    },
    {
      "step_id": "search-bar",
      "order": 1,
      "title": "Quick Search",
      "body": "<p>Press <b>/</b> to search across all your data.</p>",
      "element_target": { "element_id": "global-search" },
      "position": "bottom"
    },
    {
      "step_id": "dashboard-widget",
      "order": 2,
      "title": "Your Dashboard",
      "body": "<p>Drag and drop widgets to customise your view.</p>",
      "element_target": { "css_selector": ".dashboard-grid" },
      "position": "top",
      "optional": true
    }
  ],
  "start_page": {
    "enabled": true,
    "title": "Welcome to Your Dashboard",
    "body": "<p>Let us show you around. This tour takes about 2 minutes.</p>",
    "cta_text": "Start Tour"
  },
  "finish_page": {
    "enabled": true,
    "title": "You're All Set!",
    "body": "<p>You now know the basics. Explore on your own or revisit this tour anytime from the help menu.</p>",
    "cta_text": "Got It"
  },
  "trigger": {
    "trigger_type": "first_visit"
  },
  "audience": {
    "rules": [
      { "attribute": "plan", "operator": "in", "values": ["pro", "enterprise"] }
    ]
  }
}

Step Configuration

Each step in a tour supports the following fields:

FieldTypeRequiredDescription
step_idstringYesUnique identifier for the step
ordernumberYesZero-based position of this step within the tour
titlestringYesStep heading text
bodystringNoStep body content (supports safe HTML)
element_targetobjectYesIdentifies the DOM element this step pins to (see below)
positionstringNoTooltip position relative to the target element
optionalbooleanNoIf true, the step is skipped when the target element is missing
media_url / media_typestringNoEmbedded image or video URL + type
cta_text / cta_urlstringNoCustom call-to-action button label + link

Element target

element_target must include at least one targeting method. When more than one is given, priority is data_attributeelement_idcss_selector.

FieldTypeDescription
data_attributestringValue of a data-spotlight="…" attribute on the target. Recommended — survives refactors better than CSS classes.
element_idstringThe target's HTML id attribute (without the #).
css_selectorstringA CSS selector. selector is accepted as an alias.
descriptionstringOptional human-readable description shown in the admin overlay's target picker.
json
{ "element_target": { "data_attribute": "checkout-button" } }
{ "element_target": { "element_id": "global-search" } }
{ "element_target": { "css_selector": ".btn-primary" } }
{ "element_target": { "selector": ".btn-primary" } }   // alias of css_selector

Position

The position field controls where the tooltip appears relative to the target element:

ValueDescription
topAbove the element
bottomBelow the element (default)
leftTo the left of the element
rightTo the right of the element
autoAutomatically positioned to best fit the viewport

Positioning is handled by @floating-ui/dom, which dynamically adjusts placement if the preferred position would cause the tooltip to overflow the viewport.

Media

Embed images or videos in a step:

json
{
  "step_id": "export-step",
  "order": 0,
  "title": "Export Your Data",
  "body": "<p>Click here to export as CSV or PDF.</p>",
  "element_target": { "element_id": "export-btn" },
  "media_url": "https://cdn.example.com/export-screenshot.png",
  "media_type": "image"
}
json
{
  "media_url": "https://cdn.example.com/export-tutorial.mp4",
  "media_type": "video"
}

Custom CTA

Override the default "Next" button with a custom call-to-action:

json
{
  "step_id": "invite-team",
  "order": 0,
  "title": "Invite Your Team",
  "body": "<p>Collaboration starts here. Invite your first team member.</p>",
  "element_target": { "element_id": "invite-btn" },
  "cta_text": "Open Invite Dialog",
  "cta_url": "https://example.com/invite"
}

cta_text becomes the button label; cta_url opens that URL when clicked. Leave both off for the default "Next" behaviour. For interactive completion (e.g. wait for the user to actually click the target), use the completion field with a click_target criterion — see the Interactive Tours section below.

Start Page

The start page is a centered modal shown before the first step. It sets expectations and gives the user the choice to begin or dismiss.

json
{
  "start_page": {
    "enabled": true,
    "title": "Welcome to Your Dashboard",
    "body": "<p>We will walk you through the key features. Takes about 2 minutes.</p>",
    "cta_text": "Start Tour",
    "dismiss_text": "Maybe Later",
    "media": {
      "type": "image",
      "src": "https://cdn.example.com/welcome.png",
      "alt": "Dashboard overview illustration"
    }
  }
}

TIP

Start pages are optional. For short tours (3 steps or fewer), consider skipping the start page and diving straight into step 1.

Finish Page

The finish page appears after the user completes the final step. Use it for congratulations, next steps, or a call-to-action.

json
{
  "finish_page": {
    "enabled": true,
    "title": "Tour Complete!",
    "body": "<p>You are ready to start using the dashboard.</p>",
    "cta_text": "Done",
    "cta_url": null,
    "confetti": true
  }
}

Progress Indicator

Tours display a progress indicator showing the user's position and total steps. The progress indicator is rendered inside the Shadow DOM and is not affected by host page styles.

The indicator style is configured per tour:

StyleDescription
barHorizontal progress bar (default)
dotsStep dots showing current position
fractionText showing "Step 2 of 5"
noneNo progress indicator
json
{
  "progress_style": "dots"
}

The progress indicator uses --spot-progress-bg and --spot-progress-fill theme variables for styling.

Element Targeting and Resilience

Selector Types

Tours support any valid CSS selector. For maximum reliability, use the following priority order:

PrioritySelector TypeExampleConfidence
1data-spotlight attribute[data-spotlight='nav-menu']High
2ID selector#global-searchMedium
3Class/attribute selector.dashboard-widget[data-type='chart']Low

The admin panel displays a confidence tier icon next to each selector:

  • Green -- data-spotlight attributes. Survives CSS refactors.
  • Yellow -- ID selectors. Stable but may change with refactors.
  • Red -- Complex CSS selectors. Brittle, breaks with layout changes.

Missing Targets

When a step's target element cannot be found:

Tour SettingBehaviour
require_all_targets: trueDefer the entire tour. Retry on next page load.
require_all_targets: false (default)Skip the step and advance to the next valid step.
Per-step optional: trueSkip this step even when require_all_targets is true.

If no steps have valid targets, the tour ends gracefully with a completion page (if configured) or silently dismisses.

WARNING

Missing target elements are reported to the Spotlight backend as targeting.element_not_found events. Check the Selector Health dashboard in the admin panel to monitor targeting failures.

SPA Element Waiting

The SDK waits for target elements using a combination of MutationObserver and polling:

  1. Immediate check -- querySelector on the current DOM.
  2. MutationObserver -- Watches for DOM changes (element added by SPA rendering).
  3. Polling fallback -- querySelector every 200ms for edge cases MutationObserver misses.
  4. Timeout -- Gives up after 5 seconds (configurable) and applies skip logic.

Triggers

Tours support all 17 trigger types. Common triggers for tours:

TriggerDescription
page_loadShow when the user visits the page (default)
first_visitShow only on the user's first visit to this page
eventShow when a matching event fires
manualShow only when Spotlight.start(tourId) is called from your app code.
idleShow after N seconds of inactivity
delayShow N milliseconds after page load

See Trigger Types for the full list.

Versioning

Every time you publish a tour, a new version is created with a snapshot of the entire tour state. This enables:

  • Version history -- See what changed between publishes
  • Rollback -- Restore a previous version as a new draft
  • Re-show control -- Decide whether users who completed a previous version should see the updated tour

Force Re-Show

When force_reshow is enabled on a tour, users who completed a previous version see the entire tour again after the new version is published. When disabled (default), users who completed any version are not shown the tour again.

json
{
  "force_reshow": true
}

Keyboard Navigation

When allowKeyboardNav is enabled in the SDK init options:

KeyAction
ArrowRight / EnterNext step
ArrowLeftPrevious step
EscapeDismiss tour

Programmatic Control

js
// Start a specific tour
Spotlight.start('dashboard-onboarding');

// Listen for tour events
Spotlight.on('tour:start', (data) => {
  console.log(`Tour started: ${data.tourName}`);
});

Spotlight.on('tour:complete', (data) => {
  console.log(`Tour completed in ${data.durationMs}ms`);
});

Spotlight.on('step:view', (data) => {
  console.log(`Step ${data.stepIndex + 1}/${data.totalSteps}: ${data.stepTitle}`);
});

// Stop all active tours
Spotlight.stop();

Analytics

Tour analytics are tracked automatically:

EventDescription
tour_startedUser began the tour
tour_completedUser finished all steps
tour_dismissedUser closed the tour early
step_viewedUser viewed a specific step
step_skippedA step was skipped due to missing target
tour_deferredTour was deferred because required targets were missing

These events are visible in the admin analytics dashboard and accessible via the Events API.

Spotlight