Tour
Guided product walkthroughs — a sequence of cards that point at real elements, spotlight them, and let people move on, go back, or leave.1<TourDemo />
Anatomy
Describe a tour as an array of steps and drop in the root. With no children it renders the overlay plus the standard card.
1import { Tour } from "@raystack/apsara";23const steps = [4 { target: '[data-tour="search"]', title: "Search", content: "Find anything." },5 { target: filtersRef, title: "Filter", content: "Narrow results.", side: "right" },6 { title: "You're all set!", content: "Explore at your own pace." }, // no target → centered7];89<Tour steps={steps} defaultOpen />;
To compose the card yourself, pass the parts as children:
1<Tour steps={steps} defaultOpen>2 <Tour.Overlay />3 <Tour.Content>4 <Tour.Title />5 <Tour.Description />6 <Tour.Progress />7 <Tour.Prev />8 <Tour.Next />9 </Tour.Content>10</Tour>
API Reference
Tour
The root. Holds the steps and the open/index state, resolves targets, emits
events, and owns the actions. Renders a context provider — its children default
to <Tour.Overlay /> and <Tour.Content />.
Prop
Type
Step
Each entry in the steps array.
Prop
Type
Tour.Content
The card. Accepts positioning defaults and either static children, a render function, or the default layout.
Prop
Type
Tour.Overlay
The dimmed backdrop with a spotlight cutout over the target.
Prop
Type
Tour.Progress
Renders the step counter, e.g. "2 of 5".
Prop
Type
Tour.Title / Tour.Description
Give the card its accessible name and description, falling back to the step's
title and content.
Tour.Next / Tour.Prev / Tour.Skip / Tour.Close
Navigation buttons. Each runs your onClick first, then its action
(next, prev, skip, stop). Tour.Next finishes the tour past the last
step.
Actions
start, next, prev, go, skip, and stop. Reach them through
actionsRef, or anywhere inside <Tour> with the useTour() hook. start is
the only action that runs while the tour is closed.
Prop
Type
Events
Every lifecycle moment is reported to onEvent.
Prop
Type
Examples
Controlled with imperative controls
Control open and stepIndex, pass an actionsRef, and drive the tour from
your own UI.
1const actionsRef = useRef<TourActions>(null);2const [open, setOpen] = useState(false);3const [index, setIndex] = useState(0);45<Button onClick={() => actionsRef.current?.start()}>Take a tour</Button>67<Tour8 steps={steps}9 open={open}10 stepIndex={index}11 actionsRef={actionsRef}12 onOpenChange={(open, { status }) => {13 setOpen(open);14 if (!open) analytics.track("tour_end", { status });15 }}16 onStepChange={setIndex}17/>;
Custom card with a render function
Leave out Tour.Next and advance from app code when the user does something —
useful for action-gated steps (draw a shape, open a panel).
1<Tour steps={steps} open={open} stepIndex={index} actionsRef={actionsRef} disableOverlay>2 <Tour.Content>3 {({ step, index, totalSteps, isLastStep, actions }) => (4 <Flex direction="column" gap={3}>5 <Text weight="medium">{step.title}</Text>6 <Text variant="secondary">{step.content}</Text>7 <Flex justify="between" align="center">8 <Text size="mini">{index + 1} of {totalSteps}</Text>9 <Button size="small" onClick={isLastStep ? actions.stop : actions.next}>10 {isLastStep ? "Done" : "Next"}11 </Button>12 </Flex>13 </Flex>14 )}15 </Tour.Content>16</Tour>;1718// advance the draw step once the app reports a shape on the map19useEffect(() => {20 if (open && index === 2 && store.hasShape) actionsRef.current?.go(3);21}, [open, index, store.hasShape]);
Targets and spotlight
A target can be a CSS selector, an element, a React ref, or a function. Targets
that mount after the tour starts are awaited automatically; if one never
appears within targetTimeout the tour skips it (or stops, per
targetNotFound). If a target unmounts mid-step — its dialog closes, its route
changes — the tour recovers instead of leaving a broken overlay.
1const steps = [2 { target: "#search", title: "Selector target" },3 { target: searchRef, title: "Ref target", spotlightClicks: true },4 { target: () => editor.getNode("aoi"), title: "Function target" },5 { target: "#panel", spotlightTarget: "#panel-header", title: "Spotlight a child" },6];
Motion between steps
The dimmed backdrop stays put for the whole tour — it never flashes away between steps. Only the spotlight cutout animates: it fades shut (dimming the old target), repositions while covered, and fades back open once the next target has scrolled into view and settled, so the hole never slides across the screen or opens at a position it is about to leave.
The transition prop controls only the popover card. By default
(transition="fade") the card cross-fades with the spotlight. Set
transition="move" to glide the card smoothly from one target to the next,
which suits walkthroughs whose consecutive targets sit close together.
1<Tour steps={steps} defaultOpen transition="move" />
Both collapse to instant swaps under prefers-reduced-motion: reduce.
Accessibility
- The card is built on a Base UI
Popover, soTour.TitleandTour.Descriptionprovide its accessible name and description and it is exposed as adialog. - Focus moves into the card when a step opens and when the step changes, so
keyboard users can reach the navigation buttons. Steps with
spotlightClickskeep focus on the page so the highlighted element stays operable. - Escape ends the tour. Outside clicks and focus moves do not, so a step can keep focus on the element it highlights.
- The tour is non-modal (
modal={false}); it does not trap focus or lock scroll. - Motion — the spotlight cutout's fade, the card fade or glide, and scroll-into-view —
is enabled only when
prefers-reduced-motion: no-preference; otherwise steps swap instantly.