spotlight tours · shadcn delivery
Show people around, beautifully.
tourcn is a product-tour component for React: a spotlight overlay and a Base UI popover, positioned directly by Floating UI, installed as source code you own through the shadcn CLI.
MIT · React 19 · Tailwind v4 · no runtime package
01Playground · the tour walks through this
Monthly revenue
$12,480
+8.2% vs last month
Team
Invite a teammate
They'll get a link that expires in 7 days.
Activity
- mira published the changelog2m
- jon closed 14 issues1h
- ada shipped v0.4.03h
02Install
One command. The code lands in components/ui/tour.tsx and it's yours.
npx shadcn@latest add https://tourcn.vercel.app/r/tour.jsonDependencies (@base-ui/react, @floating-ui/react-dom, lucide-react) and the button component are resolved automatically by the CLI.
03Usage
Wrap once, describe your steps, start.
// app/layout.tsx
import { TourProvider } from "@/components/ui/tour"
export default function RootLayout({ children }) {
return <TourProvider>{children}</TourProvider>
}"use client"
import { useTour, type TourStep } from "@/components/ui/tour"
import { Button } from "@/components/ui/button"
const steps: TourStep[] = [
{ title: "Welcome to Acme", content: "Let us show you around." },
{
target: "#dashboard",
title: "Your dashboard",
content: "Everything you shipped this week, in one place.",
side: "right",
},
{
target: "#invite",
title: "Invite your team",
content: "The field stays clickable while highlighted.",
interactive: true,
},
]
export function StartTour() {
const tour = useTour()
return <Button onClick={() => tour.start(steps)}>Start tour</Button>
}04API
A surface small enough to read on one screen.
<TourProvider />
| steps | TourStep[] | Default steps used when start() is called with no arguments. |
| padding | number= 8 | Space between the target and the spotlight edge. |
| radius | number= 10 | Spotlight corner radius in pixels. |
| interactive | boolean= true | Whether highlighted elements stay clickable through the overlay. |
| scrollIntoView | ScrollIntoViewOptions | false= smooth, center | How each target is scrolled into view. |
| keyboard | boolean= true | Arrow-key navigation and Escape to close. |
| overlayClick | "stop" | "next" | "none"= "stop" | What clicking the dimmed overlay does. |
| labels | { back, next, done } | Button labels, for localization. |
| onStepChange | (index, step) => void | Fires whenever the active step changes. |
| onEnd | (reason: "finished" | "stopped") => void | Fires when the tour completes or is dismissed. |
TourStep
| target | string | ref | () => Element | What to highlight. Omit for a centered step over a fully dimmed page. |
| title | ReactNode | Popover heading. |
| content | ReactNode | Popover body. |
| side | "top" | "right" | "bottom" | "left"= "bottom" | Preferred popover side; flips on collision. |
| align | "start" | "center" | "end"= "center" | Alignment along the chosen side. |
| padding / radius | number | Per-step spotlight overrides. |
| interactive | boolean | Per-step override for click-through. |
| render | (ctx) => ReactNode | Replace the entire popover body; ctx carries next, prev, stop, goTo, index… |
| onEnter | ({ index }) => void | Fires when the step becomes active. |
useTour()
| start | (steps?, index?) => void | Begin a tour with the provider's default steps or ad-hoc ones. |
| next / prev / goTo | () => void | Navigate between steps. |
| stop | () => void | End the tour immediately. |
| active / index / total / step | state | Reactive state of the running tour. |