Tour React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React product tour that dims the page around the real UI, explains each step in a card, docks at the bottom on phones and adds hotspots.
Start the tour with its button or the shortcut and the page dims around the first target: a rounded spotlight hugs the element and a card beside it explains the step. Next, Back and Skip sit in the card; the progress dots can be clicked to jump. Each step scrolls its target into view first, then the spotlight glides there on a spring and the card fades in beside it; steps without a target show a centered card. Keys: Right arrow or Enter goes next, Left goes back, Escape skips (announced), Tab stays inside the card, and focus returns to where it was when the tour ends. Interactive steps let clicks and typing reach the highlighted element, and advanceOn click moves on when you press the real button. On a phone the card docks at the bottom above the safe area and the keyboard, its target is scrolled into the top half, and a swipe left or right pages the steps. With persistKey a finished or skipped tour is remembered and autoStart leaves it alone. TourHotspot is a pulsing dot on a new feature: tap or click it for a hint (a bottom card on phones); once seen it stays away. Reduced motion makes the spotlight jump and the cards fade.
Tour is a Pro React component in the React overlay components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add tour, together with Button, Kbd, Progress, useControllableState, useKeyboardInset and 7 more and the npm packages @radix-ui/react-popover, motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add tourTour is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import Tour from @/components/ui/tour.
import { useRef } from "react"
import { Button } from "@/components/ui/button"
import { Tour, type TourStep } from "@/components/ui/tour"
export function ClientsTour() {
const search = useRef<HTMLInputElement>(null)
const invoice = useRef<HTMLButtonElement>(null)
const steps: TourStep[] = [
{ id: "welcome", title: "Welcome to Northwind CRM", content: "We show you where everything is in 30 seconds." },
{ id: "search", target: search, title: "Find any client", content: "A name, an EIN or a city." },
{ id: "invoice", target: invoice, title: "Issue an invoice", content: "Press New invoice.", advanceOn: "click" },
]
return (
<Tour
steps={steps}
persistKey="northwind-crm:tour"
autoStart
shortcut="shift+h"
labels={{ next: "Continue" }}
>
<input ref={search} placeholder="Search clients" />
<Button ref={invoice}>New invoice</Button>
</Tour>
)
}Props
steps*TourStep[]The steps: { id, target? (selector, function or ref), title, content?, media?, side? (top / right / bottom / left / auto), align?, spotlightPadding?, spotlightRadius?, interactive?, advanceOn?: "click", beforeEnter?, nextLabel? }. A step without a target, or whose target is not found within 1s, shows a centered card.
defaultOpendefaultfalsebooleanStarts open. Use open / onOpenChange to control it, or start() from useTour / setApi.
openbooleanControlled open state. Only one tour shows at a time; a second one waits for the first to close.
onOpenChange(open: boolean) => voidCalled when the tour opens or closes.
defaultStepdefault0numberThe step index it starts at.
stepnumberControlled step index.
onStepChange(step: number) => voidCalled with the new index on Next, Back, a dot, a swipe or goTo.
onComplete() => voidCalled when the last step's Done is pressed (or finish()).
onSkip() => voidCalled on Skip, the close button, Escape, an overlay click with closeOnOverlayClick, or skip().
persistKeydefault""stringLocal storage key that remembers the tour was completed or skipped. autoStart does not start a finished tour; start() still does, and reset() forgets it.
autoStartdefaultfalsebooleanOpens once after hydration and a 600ms settle, unless persistKey says it was already finished.
shortcutdefault""stringA global key that starts the tour, in mod+k notation. Empty binds nothing; the demo passes "shift+h" (the showcase owns ?).
spotlightdefaulttruebooleanDims the page with a rounded cutout around the target and blocks clicks elsewhere. false shows only the cards, with an arrow (a ring alone marks the target when the card is docked), and leaves the page usable (coachmarks).
overlayOpacitydefault0.55numberStrength of the dim. Dark mode adds 0.1 so the page still recedes behind the card.
spotlightPaddingdefault8numberRoom in px between the target and the edge of the cutout. Steps can override it.
spotlightRadiusdefault14numberCorner radius of the cutout in px, capped at half the smaller side. Steps can override it.
showProgressdefault"dots""dots" | "fraction" | "bar" | "none"How the card shows where you are: dots (clickable), 2 / 5, a thin bar, or nothing (screen readers still hear it).
sizedefault"md""sm" | "md"Card width 288 / 340px (never wider than the screen minus 24px), with matching padding and type.
radiusdefault"lg""none" | "sm" | "md" | "lg" | "full"Card corners: 0, 12, 16 or 24px; full falls back to lg. The docked phone card keeps its 28px sheet top.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Color of the Next button and the progress.
colordefault""stringAny CSS color for the Next button and the progress; replaces the tone.
showSkipdefaulttruebooleanA Skip button in the card on every step but the last.
showClosedefaulttruebooleanThe × in the card's corner (on phones it shows only when Skip is hidden). It skips the tour.
keyHintsdefaulttruebooleanShows the → key on the Next button on desktops. Hidden on touch screens.
closeOnOverlayClickdefaultfalsebooleanA click on the dimmed area skips the tour. Off by default so a stray tap does not end it.
scrollBehaviordefault"smooth""smooth" | "instant"How targets scroll into view (instant under reduced motion). Nested scrollers scroll too.
mobileLayoutdefault"docked""docked" | "popover"Below 768px the card docks at the bottom where the thumb is, with a 44px Next and swipe paging, and targets scroll into the top half. popover keeps the desktop card.
dockeddefaultfalsebooleanDocks the card at every width, e.g. inside a phone mockup.
containerElement | nullWhere the overlay renders (default document.body). A transformed element becomes the frame: the dim, the spotlight and the docked card stay inside it.
setApi(api: TourApi) => voidReceives { start(stepId?), next(), previous(), goTo(index | id), skip(), finish(), reset() } once mounted.
labelsPartial<TourLabels>Every string: next "Next", back "Back", skip "Skip tour", done "Done", close "Close tour", progress(i, n) "Step i of n", stepAnnounce(i, n, title), skipped "Tour closed", completed "Tour completed". DEFAULT_TOUR_LABELS has the English set.
classNamesPartial<Record<"overlay" | "spotlight" | "card" | "media" | "title" | "content" | "progress" | "actions" | "close" | "handle", string>>Extra classes for the inner parts.
childrenReactNodeRendered in place; anything inside can read the tour with useTour().
Parts
TourHotspot
componentA pulsing dot on a corner of its children (or of target) that opens a hint: title, content, placement ("top-end" | "top-start" | "bottom-end" | "bottom-start"), offset, side, align, persistKey (gone once seen), tone, color, pulse, open / defaultOpen / onOpenChange, onDismiss, responsive (a bottom card below 768px), labels { open, dismiss "Got it" }, classNames. Props: TourHotspotProps.
useTour
hookThe nearest tour's API plus { open, step, count, status }; null outside a Tour. Signature: () => TourState | null.
DEFAULT_TOUR_LABELS
constantThe English card strings: next, back, skip "Skip tour", done, close, progress and the screen reader announcements. Override keys through labels.
DEFAULT_TOUR_HOTSPOT_LABELS
constantThe hotspot strings: open "Show tip" (the dot's accessible name) and dismiss "Got it". Override them through TourHotspot labels.
TourStep
typeOne step: id, target (selector, function or ref), title, content, media, side, align, interactive, advanceOn, beforeEnter and nextLabel.
TourApi
typeThe controls setApi hands you: start(stepId?), next, previous, goTo(index or id), skip, finish and reset.
TourHotspotProps
typeThe props of TourHotspot, for wrappers that render hints from your own data.
Examples
Progress bar, small card
showProgress="bar" with size="sm": 288px cards for short, three-step tours.
Coachmarks without dimming
spotlight={false}: no overlay, the page stays usable and each card points at its target with an arrow.
Hotspot
TourHotspot pulses on a new feature; opened once, persistKey keeps it away. Tap it on a phone for a bottom card.
Interactive step
interactive: the search takes the caret and typing reaches the page while the tour waits.
Phone frame, docked and swipeable
docked inside a phone mockup (container): the card sits at the bottom, swipe it left or right to page.
beforeEnter opens a menu
The second step opens the Actions menu before it highlights Export to Excel; the third closes it again.
Dependencies
Files
- components/ui/tour.tsx
FAQ
Is the React Tour component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Tour?
Run npx wingo-ui@latest add tour. It also adds the Wingo UI items it builds on (Button, Kbd, Progress, useControllableState, useKeyboardInset and 7 more) and the npm packages @radix-ui/react-popover, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/tour.
How does Tour behave on phones?
On a phone the card docks at the bottom above the safe area and the keyboard, its target is scrolled into the top half, and a swipe left or right pages the steps. TourHotspot is a pulsing dot on a new feature: tap or click it for a hint (a bottom card on phones); once seen it stays away.
See also
More React overlay components
Articles using this component
- Onboarding Checklist UI: The Get Started Card in React
Build it: screens and apps9 min read
- React Product Tour: Spotlight, Phone Dock, Hotspots
Component guides9 min read