tourcn

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.

Install

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.

terminal
npx shadcn@latest add https://tourcn.vercel.app/r/tour.json

Dependencies (@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
// app/layout.tsx
import { TourProvider } from "@/components/ui/tour"

export default function RootLayout({ children }) {
  return <TourProvider>{children}</TourProvider>
}
components/tour.tsx
"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 />

stepsTourStep[]Default steps used when start() is called with no arguments.
paddingnumber= 8Space between the target and the spotlight edge.
radiusnumber= 10Spotlight corner radius in pixels.
interactiveboolean= trueWhether highlighted elements stay clickable through the overlay.
scrollIntoViewScrollIntoViewOptions | false= smooth, centerHow each target is scrolled into view.
keyboardboolean= trueArrow-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) => voidFires whenever the active step changes.
onEnd(reason: "finished" | "stopped") => voidFires when the tour completes or is dismissed.

TourStep

targetstring | ref | () => ElementWhat to highlight. Omit for a centered step over a fully dimmed page.
titleReactNodePopover heading.
contentReactNodePopover body.
side"top" | "right" | "bottom" | "left"= "bottom"Preferred popover side; flips on collision.
align"start" | "center" | "end"= "center"Alignment along the chosen side.
padding / radiusnumberPer-step spotlight overrides.
interactivebooleanPer-step override for click-through.
render(ctx) => ReactNodeReplace the entire popover body; ctx carries next, prev, stop, goTo, index…
onEnter({ index }) => voidFires when the step becomes active.

useTour()

start(steps?, index?) => voidBegin a tour with the provider's default steps or ad-hoc ones.
next / prev / goTo() => voidNavigate between steps.
stop() => voidEnd the tour immediately.
active / index / total / stepstateReactive state of the running tour.