Component reference
Tour
A controlled product tour with real targets.
Preview
Live previewOptional six-digit hex. Clear to restore the preset. Check contrast with your brand color.
Changes apply to this preview only.
Workspace
The highlighted regions are ordinary components; the tour only reads their rectangles.
Width adapts to the available space
"use client";
import {
Button,
Badge,
type PimoluTourStep,
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
Input,
Tour
} from "@pimolu/react";
import { useState, useRef } from "react";
const activity = [
{ name: "Ada Lovelace", action: "shipped the Q3 report" },
{ name: "Noah Dubois", action: "invited two teammates" },
{ name: "Maya Chen", action: "connected a data source" },
];
export function DemoExample() {
const searchRef = useRef<HTMLDivElement | null>(null);
const actionsRef = useRef<HTMLDivElement | null>(null);
const activityRef = useRef<HTMLDivElement | null>(null);
const [open, setOpen] = useState(false);
const [step, setStep] = useState(0);
const [status, setStatus] = useState("Tour not started");
const steps: PimoluTourStep[] = [
{
target: searchRef,
title: "Find anything",
description: "Search projects, documents and teammates from one field.",
placement: "bottom",
},
{
target: actionsRef,
title: "Start something new",
description: "Create a project or invite a teammate without leaving the page.",
placement: "left",
},
{
target: activityRef,
title: "Stay in the loop",
description: "Recent activity lands here, with a link to the full feed.",
placement: "top",
},
];
function startTour() {
setStep(0);
setStatus("Tour running");
setOpen(true);
}
return (
<div className="grid gap-5 min-w-0">
<div
className="flex flex-wrap items-center gap-3 min-w-0 justify-between"
style={{
flexWrap: "wrap"
}}
>
<div className="flex flex-wrap items-center gap-3 min-w-0">
<Button onClick={startTour}>Start tour</Button>
<Badge variant="neutral">
{status}
</Badge>
{
open && (<Badge variant="primary">
step {step + 1} of {steps.length}
</Badge>)
}
</div>
<span className="text-sm text-muted leading-relaxed">ArrowLeft / ArrowRight also move between steps.</span>
</div>
<Card>
<CardHeader>
<CardTitle>Workspace</CardTitle>
<CardDescription>
The highlighted regions are ordinary components; the tour only
reads their rectangles.
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-5 min-w-0">
<div ref={searchRef}>
<Input
label="Search the workspace"
placeholder="Projects, people, files…"
/>
</div>
<div
ref={actionsRef}
className="flex flex-wrap items-center gap-3 min-w-0"
>
<Button size="sm">New project</Button>
<Button size="sm" appearance="outline">Invite teammate</Button>
</div>
<div
ref={activityRef}
className="grid gap-5 min-w-0"
style={{
padding: 14,
border: "1px solid var(--pimolu-border)",
borderRadius: "var(--pimolu-radius-md)",
}}
>
<strong className="mb-2 text-lg">Recent activity</strong>
{
activity.map((item) => (<div
key={item.name}
className="flex flex-wrap items-center gap-3 min-w-0 justify-between"
>
<span>{item.name}</span>
<span className="text-sm text-muted leading-relaxed">
{item.action}
</span>
</div>))
}
</div>
</div>
</CardContent>
</Card>
<Tour
open={open}
steps={steps}
onOpenChange={(next) => {
setOpen(next);
if(!next) {
setStatus((current) => current === "Tour running"
? "Tour closed"
: current);
}
}}
onStepChange={setStep}
onFinish={() => setStatus("Tour completed")}
onSkip={() => setStatus("Tour skipped")}
nextLabel="Continue"
prevLabel="Go back"
finishLabel="Got it"
skipLabel="Maybe later"
/>
</div>
);
}More examples
Different configurations and compositions using the real Pimolu API. Each preview runs independently and follows the site appearance.
Focused one-step introduction
A controlled tour highlights real targets and reports completion without changing global state.
Localized tour controls
A controlled tour highlights real targets and reports completion without changing global state.
String selector and custom labels
target="#landing-readiness" resolves after mount. showSkip turns off the skip control and finishLabel names the last action.
Usage
Keep targets mounted and provide an exit path; onboarding must not block essential tasks.
API reference
Generated from the public TypeScript declarations. Native HTML attributes are inherited where the component extends them; this reference includes Pimolu props and selected common native attributes.
PimoluTourStep
Type source| Name | Type | Default | Description |
|---|---|---|---|
targetRequired | PimoluTourTarget | — | Public Pimolu prop; see the source declaration for details. |
titleRequired | ReactNode | — | Heading of the step. It names the dialog. |
description | ReactNode | — | Optional body copy under the heading. |
placement | PimoluTourPlacement | undefined | "bottom" | Preferred side of the target. Defaults to "bottom". |
targetRequiredPimoluTourTargetNo default extracted.
Public Pimolu prop; see the source declaration for details.
titleRequiredReactNodeNo default extracted.
Heading of the step. It names the dialog.
descriptionReactNodeNo default extracted.
Optional body copy under the heading.
placementPimoluTourPlacement | undefinedDefault: "bottom"
Preferred side of the target. Defaults to "bottom".
PimoluTourProps
Type source| Name | Type | Default | Description |
|---|---|---|---|
stepsRequired | PimoluTourStep[] | — | Public Pimolu prop; see the source declaration for details. |
open | boolean | undefined | — | Controlled open state. Omit to let the component own it. |
defaultOpen | boolean | undefined | false | Initial open state when uncontrolled. Defaults to false. |
onOpenChange | ((open: boolean) => void) | undefined | — | Called whenever the tour asks to open or close. |
onFinish | (() => void) | undefined | — | Runs when the last step is completed. |
onSkip | (() => void) | undefined | — | Optional callback when the tour is skipped. |
onStepChange | ((index: number) => void) | undefined | — | Called with the new zero-based index when the step changes. |
initialStep | number | undefined | 0 | Step shown when the tour opens. Defaults to 0. |
showSkip | boolean | undefined | true | Show the skip control. Defaults to true. |
nextLabel | string | undefined | "Next" | Label of the next action. Defaults to "Next". |
prevLabel | string | undefined | "Back" | Label of the previous action. Defaults to "Back". |
finishLabel | string | undefined | "Finish" | Label of the last action. Defaults to "Finish". |
skipLabel | string | undefined | "Skip" | Label of the skip action. Defaults to "Skip". |
className | string | undefined | — | Class applied to the fixed tour layer. |
stepsRequiredPimoluTourStep[]No default extracted.
Public Pimolu prop; see the source declaration for details.
openboolean | undefinedNo default extracted.
Controlled open state. Omit to let the component own it.
defaultOpenboolean | undefinedDefault: false
Initial open state when uncontrolled. Defaults to false.
onOpenChange((open: boolean) => void) | undefinedNo default extracted.
Called whenever the tour asks to open or close.
onFinish(() => void) | undefinedNo default extracted.
Runs when the last step is completed.
onSkip(() => void) | undefinedNo default extracted.
Optional callback when the tour is skipped.
onStepChange((index: number) => void) | undefinedNo default extracted.
Called with the new zero-based index when the step changes.
initialStepnumber | undefinedDefault: 0
Step shown when the tour opens. Defaults to 0.
showSkipboolean | undefinedDefault: true
Show the skip control. Defaults to true.
nextLabelstring | undefinedDefault: "Next"
Label of the next action. Defaults to "Next".
prevLabelstring | undefinedDefault: "Back"
Label of the previous action. Defaults to "Back".
finishLabelstring | undefinedDefault: "Finish"
Label of the last action. Defaults to "Finish".
skipLabelstring | undefinedDefault: "Skip"
Label of the skip action. Defaults to "Skip".
classNamestring | undefinedNo default extracted.
Class applied to the fixed tour layer.
Accessibility
- Verify dialog names, step focus, Escape, completion and missing-target behavior.