Step Tour
Guided, step-by-step product tours that highlight DOM elements with tooltips. Ideal for onboarding new users or showcasing features after a release.
Basic Tour
A realistic dashboard mock-up with five tour steps covering the header, stats panel, action bar, sidebar navigation, and profile menu.
Interactive dashboard tour
Click 'Start Tour' to walk through each highlighted element.
import { StepTour } from 'fluxo-ui';
import type { TourStep } from 'fluxo-ui/types';
const steps: TourStep[] = [
{
selector: '#dashboard-header',
title: 'Dashboard Overview',
content: 'This is your main dashboard with key metrics.',
placement: 'bottom',
order: 1,
},
{
selector: '#sidebar-nav',
title: 'Navigation',
content: 'Jump to any section from this sidebar.',
placement: 'right',
order: 2,
},
];
const [isOpen, setIsOpen] = useState(false);
<Button onClick={() => setIsOpen(true)}>Start Tour</Button>
{isOpen && (
<StepTour
steps={steps}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
/>
)}Rich JSX Content
Step content supports full JSX, allowing lists, callout boxes, badges, and any custom markup.
Steps with rich JSX content
Tour steps can contain lists, callouts, icons, and styled elements.
const steps: TourStep[] = [
{
selector: '#export-button',
title: '๐ New Feature',
content: (
<div className="space-y-2 text-sm">
<p>This powerful feature lets you:</p>
<ul className="list-disc list-inside space-y-1 text-gray-500">
<li>Export data in multiple formats</li>
<li>Schedule automated reports</li>
</ul>
</div>
),
placement: 'bottom',
},
{
selector: '#config-panel',
title: 'โ๏ธ Configuration',
content: (
<div className="space-y-2 text-sm">
<p>Configure this panel to match your workflow.</p>
<div className="p-2 rounded bg-yellow-50 text-yellow-800 text-xs">
Tip: Changes are saved automatically.
</div>
</div>
),
placement: 'right',
},
];Step Callbacks
Every step exposes onNext, onPrev, and onSkip callbacks. Use them to sync external state, fire analytics events, or trigger side effects at each transition.
Per-step lifecycle callbacks
Start the tour and navigate between steps โ live events appear in the log below.
const steps: TourStep[] = [
{
selector: '#feature-a',
title: 'Feature A',
content: 'This is Feature A.',
onNext: () => analytics.track('tour_step_next', { step: 1 }),
onSkip: () => analytics.track('tour_skip', { step: 1 }),
},
{
selector: '#feature-b',
title: 'Feature B',
content: 'This is Feature B.',
onPrev: () => analytics.track('tour_step_prev', { step: 2 }),
onNext: () => analytics.track('tour_step_next', { step: 2 }),
},
];Placement Options
Each step declares a preferred placement. The tooltip automatically falls back to an alternative side when viewport space is insufficient.
Tooltip placements โ top ยท bottom ยท left ยท right
Tour through the four placement variants. Watch the tooltip anchor to each side.
const steps: TourStep[] = [
{ selector: '#el-top', title: 'Top', content: '...', placement: 'top' },
{ selector: '#el-bottom', title: 'Bottom', content: '...', placement: 'bottom' },
{ selector: '#el-left', title: 'Left', content: '...', placement: 'left' },
{ selector: '#el-right', title: 'Right', content: '...', placement: 'right' },
];
// The tooltip falls back gracefully when the viewport has insufficient space.Start at a Specific Step
Pass initialStep to resume a tour mid-way โ useful for restoring progress from local storage or deep-linking into a feature.
Resuming from an arbitrary step
Choose a starting step index (0-based) then launch the tour.
const steps: TourStep[] = [
{ selector: '#header', title: 'Dashboard Overview', content: '...', placement: 'bottom' },
{ selector: '#stats', title: 'Key Metrics', content: '...', placement: 'bottom' },
{ selector: '#actions', title: 'Quick Actions', content: '...', placement: 'top' },
{ selector: '#sidebar', title: 'Navigation', content: '...', placement: 'right' },
{ selector: '#profile', title: 'Your Profile', content: '...', placement: 'left' },
];
// Restore progress from storage
const savedStep = parseInt(localStorage.getItem('tour-step') ?? '0', 10);
{isOpen && (
<StepTour
steps={steps}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
initialStep={savedStep}
/>
)}Dark Mode Support
The tour overlay, highlight, and tooltip all respond to the mode-dark class on document.body. No extra props needed โ the theme is inherited automatically via Tailwind's dark-variant utilities.
API Reference
StepTour props
stepsreqTourStep[]Ordered list of tour steps. Each step targets a DOM element via a CSS selector.
stepsreqTourStep[]Ordered list of tour steps. Each step targets a DOM element via a CSS selector.
isOpenreqbooleanControls whether the tour is visible.
isOpenreqbooleanControls whether the tour is visible.
onClosereq() => voidCalled when the user skips or completes the tour.
onClosereq() => voidCalled when the user skips or completes the tour.
initialStepnumber"0"Zero-based index of the step to start from.
initialStepnumber"0"Zero-based index of the step to start from.
zIndexnumber"10050"Base z-index for the tour overlay, highlight, and tooltip layers. Defaults above modals/drawers per popover rules.
zIndexnumber"10050"Base z-index for the tour overlay, highlight, and tooltip layers. Defaults above modals/drawers per popover rules.
ariaLabelstring"'Product tour'"Accessible name for the tour dialog read by screen readers.
ariaLabelstring"'Product tour'"Accessible name for the tour dialog read by screen readers.
interactiveHighlightboolean"false"When true, clicks pass through the highlight overlay so users can interact with the highlighted element during the tour.
interactiveHighlightboolean"false"When true, clicks pass through the highlight overlay so users can interact with the highlighted element during the tour.
onMissingStep(stepIndex: number, step: TourStep) => voidCalled when a step's selector does not match any element so consumers can advance, retry, or surface the issue.
onMissingStep(stepIndex: number, step: TourStep) => voidCalled when a step's selector does not match any element so consumers can advance, retry, or surface the issue.
TourStep
selectorreqstringCSS selector that identifies the target DOM element for this step.
selectorreqstringCSS selector that identifies the target DOM element for this step.
titleReactNodeOptional heading shown at the top of the tooltip.
titleReactNodeOptional heading shown at the top of the tooltip.
contentreqReactNodeBody content of the step tooltip. Supports JSX.
contentreqReactNodeBody content of the step tooltip. Supports JSX.
placement'top' | 'bottom' | 'left' | 'right'"'bottom'"Preferred side for the tooltip relative to the target element. Falls back automatically if there is not enough space.
placement'top' | 'bottom' | 'left' | 'right'"'bottom'"Preferred side for the tooltip relative to the target element. Falls back automatically if there is not enough space.
ordernumberExplicit sort order for this step (defaults to array index position).
ordernumberExplicit sort order for this step (defaults to array index position).
onNext() => voidCallback fired when the user advances past this step.
onNext() => voidCallback fired when the user advances past this step.
onPrev() => voidCallback fired when the user goes back to this step.
onPrev() => voidCallback fired when the user goes back to this step.
onSkip() => voidCallback fired when the user skips the tour from this step.
onSkip() => voidCallback fired when the user skips the tour from this step.
Features
DOM Element Targeting
Each step targets any DOM element via a CSS selector, overlaying a highlight with a tooltip.
Rich JSX Content
Step content accepts any React node โ lists, callouts, badges, icons, and custom markup.
Four Placements
Tooltip placement can be top, bottom, left, or right with automatic fallback when space is limited.
Step Callbacks
onNext, onPrev, and onSkip callbacks on each step enable analytics, state sync, and side effects.
Resume from Any Step
The initialStep prop lets you deep-link into a specific step or restore saved tour progress.
Overlay & Highlight
A semi-transparent overlay dims the rest of the page while the target element is highlighted with a border.
Dark Mode
Overlay, highlight, and tooltip all adapt automatically to dark/light mode via CSS class inheritance.
Accessibility
Keyboard navigation and focus management ensure the tour is fully accessible for all users.