@maskxin/react-spotlight-tour
v0.1.1
Published
A spotlight-based guided tour component for React apps. Headless hook + default Tailwind UI included.
Maintainers
Readme
react-spotlight-tour
A spotlight-based guided tour component for React apps. Highlights target elements with a spotlight overlay and shows step-by-step popover cards.
- Headless hook (
useTour) — bring your own UI - Default Tailwind UI (
TourOverlay+TourCard) — drop in and go - Zero runtime deps — only React as a peer dependency
- localStorage persistence — remembers when the tour was completed
- Responsive positioning — auto-adjusts card placement to fit viewport
- Reveal support — auto-expands collapsed sections before spotlighting
Install
npm install react-spotlight-tour
# or
pnpm add react-spotlight-tourPeer dependencies:
react >= 18andreact-dom >= 18The default
TourCarduses Tailwind CSS classes. If you use the default card, make sure Tailwind scansnode_modules/react-spotlight-tour(or add the package to yourcontentconfig).
Quick Start
import { TourProvider, TourOverlay } from "react-spotlight-tour";
import type { TourStep } from "react-spotlight-tour";
const steps: TourStep[] = [
{
id: "welcome",
title: "Welcome!",
description: "This is your dashboard. Let's take a quick tour.",
targetSelectors: ['[data-tour-id="dashboard"]'],
placement: "center",
},
{
id: "sidebar",
title: "Navigation",
description: "Use the sidebar to switch between sections.",
targetSelectors: ['[data-tour-id="sidebar"]'],
placement: "right",
spotlightPadding: 4,
},
{
id: "create-button",
title: "Create Something",
description: "Click here to create your first item.",
targetSelectors: ['[data-tour-id="create-btn"]'],
placement: "left",
},
];
function App() {
return (
<TourProvider steps={steps} onComplete={() => console.log("Tour done!")}>
<TourOverlay />
<YourApp />
</TourProvider>
);
}Then add data-tour-id attributes to the target elements in your app:
<aside data-tour-id="sidebar">...</aside>
<button data-tour-id="create-btn">Create</button>API Reference
<TourProvider>
Wraps your app and provides tour state via React context.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| steps | TourStep[] | required | Ordered list of tour steps |
| autoShow | boolean | true | Auto-show on mount if not completed |
| storage | TourStorageOptions | — | Customize localStorage key |
| onStart | (trigger) => void | — | Called when tour starts |
| onStepChange | (index, direction) => void | — | Called on step navigation |
| onComplete | () => void | — | Called when user finishes the tour |
| onSkip | (stepIndex) => void | — | Called when user skips/closes early |
useTour()
Hook to access tour state and controls. Must be inside <TourProvider>.
const {
isActive, // boolean — is the tour showing?
currentStepIndex,// number — zero-based step index
currentStep, // TourStep | undefined
totalSteps, // number
trigger, // "auto" | "manual" | null
next, // () => void
back, // () => void
skip, // () => void — close & mark completed
finish, // () => void — finish on last step
start, // (trigger?) => void — restart the tour
} = useTour();<TourOverlay>
Default spotlight overlay. Renders the backdrop, spotlight ring, and card.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| CardComponent | ComponentType<TourCardProps> | TourCard | Custom card component |
| backdropZIndex | number | 40 | z-index for the backdrop |
| cardZIndex | number | 70 | z-index for the card |
| spotlightBorderColor | string | "rgba(59,130,246,0.5)" | Spotlight ring color |
| overlayColor | string | "rgba(0,0,0,0.25)" | Overlay backdrop color |
<TourCard>
Default card UI. You can use it standalone or replace it via TourOverlay's CardComponent prop.
TourStep
interface TourStep {
id: string;
title: string;
description: string;
tip?: string;
targetSelectors: string[];
spotlightPadding?: number; // default: 10
revealSelectors?: string[];
placement?: "center" | "right" | "left" | "top";
}Headless Mode
Use useTour() with your own UI — skip <TourOverlay> entirely:
import { TourProvider, useTour } from "react-spotlight-tour";
function MyCustomTourUI() {
const { isActive, currentStep, next, back, skip, finish } = useTour();
if (!isActive || !currentStep) return null;
return (
<div className="my-custom-tour-popover">
<h3>{currentStep.title}</h3>
<p>{currentStep.description}</p>
<button onClick={back}>Back</button>
<button onClick={next}>Next</button>
<button onClick={skip}>Skip</button>
</div>
);
}
function App() {
return (
<TourProvider steps={steps}>
<MyCustomTourUI />
<YourApp />
</TourProvider>
);
}Custom Card Component
Replace only the card while keeping spotlight positioning:
import { TourOverlay, type TourCardProps } from "react-spotlight-tour";
function MyCard({ step, currentStep, totalSteps, onNext, onBack, onClose, onFinish }: TourCardProps) {
return (
<div className="my-card">
<h3>{step.title}</h3>
<p>{step.description}</p>
<span>{currentStep + 1} / {totalSteps}</span>
<button onClick={onNext}>Next</button>
</div>
);
}
// In your app:
<TourOverlay CardComponent={MyCard} />Programmatic Control
function SettingsPage() {
const { start } = useTour();
return (
<button onClick={() => start("manual")}>
Replay tour
</button>
);
}Storage
By default, completion is persisted to localStorage under the key "react-spotlight-tour.completed". Customize it:
<TourProvider
steps={steps}
storage={{
storageKey: "my-app.tour.completed.v1",
legacyKeys: ["old-app.onboarding.done"],
}}
>Use clearCompleted() to reset:
import { clearCompleted } from "react-spotlight-tour";
clearCompleted("my-app.tour.completed.v1");Tailwind Setup
Add the package to your Tailwind content config so the default card styles are included:
// tailwind.config.js
export default {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/react-spotlight-tour/dist/**/*.js",
],
};License
MIT
