GuidedTour
A multi-step tour anchored to real elements, with the steps declared as data.
@luc/uiExamples
A tour that points at the product
Three anchored steps and a targetless summary. The spotlight is measured from the real element on every scroll and resize, Escape leaves from any step, and focus returns to the button you started from.
Runs
btc-sma-2026-09 · finished · 4 artifacts
const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Start the tour</Button>
<GuidedTour open={open} onOpenChange={setOpen} steps={steps} />A step whose target is gone
A selector that resolves to nothing gives a centred card instead of a spotlight over the top-left corner — the tour degrades, it does not break.
Runs
btc-sma-2026-09 · finished · 4 artifacts
<GuidedTour
open={open}
onOpenChange={setOpen}
steps={[{ id: "gone", title: "…", target: "#tour-does-not-exist" }]}
/>A targetless step
Omit `target` for a step that is a summary rather than a pointer: it centres itself and still owns focus, Escape and the step counter.
Runs
btc-sma-2026-09 · finished · 4 artifacts
<GuidedTour
open={open}
onOpenChange={setOpen}
steps={[{ id: "summary", title: "That is the whole tour" }]}
/>Usage
import { GuidedTour } from "@luc/ui";const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Start tour</Button>
<GuidedTour open={open} onOpenChange={setOpen} steps={steps} />A step's `target` is measured on every scroll and resize; omit it for a centred, targetless step.
Package setup — peer requirements, the stylesheet and the provider — is on installation.
API
Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.
One component and one step shape. `stepIndex` makes the current step controlled, `labels` localises the controls and the counter, and a step's `target` is a CSS selector — the contract is that the element must exist while the tour is open.
| Export | Kind | Signature |
|---|---|---|
GuidedTour | 8 documented props | ({ steps, open, onOpenChange, stepIndex, defaultStepIndex, onStepChange, labels, className, }: GuidedTourProps) => import("@types/react").JSX.Element | null |
GuidedTour props
Type: GuidedTourProps
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
className? | string | undefined | — | |
defaultStepIndex? | number | undefined | — | |
labels? | GuidedTourLabels | undefined | — | |
onOpenChange | (open: boolean) => void | — | |
onStepChange? | ((index: number) => void) | undefined | — | |
open | boolean | — | |
stepIndex? | number | undefined | — | |
steps | readonly TourStep[] | — |
Guidelines
Do
- Anchor a step to a stable selector — an id or a data attribute — never to a class that styling can change.
- Keep a tour to three or five steps with one idea each, and let the last one be targetless if it is a summary.
- Give a step a `placement` when the default would cover the thing it points at.
Don't
- Do not tour a feature the reader has not opened yet — the spotlight needs something to point at.
- Do not use it as a first-run modal: this one can be dismissed from the keyboard, and a modal that cannot is a trap.
- Do not target an element inside a closed overlay; the selector resolves to nothing and the step loses its anchor.
Accessibility
The card is a modal dialog: focus moves into it when the tour opens and returns to the trigger when it closes, Escape leaves from any step, and Tab is kept inside the card while it is up. The spotlight is decorative and `aria-hidden`, so a reader hears the step's own title and description rather than the shape of the highlight — and a step whose target has disappeared falls back to a centred card instead of failing.
Family
Feedback
What the product says when something happened, is happening, or did not.
10 documented entries in this family. Open the family index
Related