Tour
tourGuides users through anchored steps with a cutout highlight, positioned card, navigation, and progress.
Usage
Basic boot
Controlled open + current; steps uses the target function to return the ref element, step by step highlighting (including the centered opening step without target). Click "Start Boot" to try.
Click the button below to start the novice guide.
const searchRef = useRef<HTMLDivElement>(null);
const [open, setOpen] = useState(false);
const [current, setCurrent] = useState(0);
const steps = [
{ title: "Welcome to Hulian", description: "Take you to know the three core functions in 30 seconds." },
{
target: () => searchRef.current,
title: "Global Search",
description: "Quickly locate any resource by keyword.",
placement: "bottom",
},
];
<>
<div ref={searchRef}>Search for resources...</div>
<Button onClick={() => { setCurrent(0); setOpen(true); }}>Start boot</Button>
<Tour
steps={steps}
open={open}
current={current}
onChange={setCurrent}
onClose={() => setOpen(false)}
/>
</>Point mask off
maskClosable allows you to click on the dark mask area to directly end the boot (default false, to prevent accidental touch).
Click the button below to start the novice guide.
<Tour
steps={steps}
open={open}
current={current}
onChange={setCurrent}
onClose={() => setOpen(false)}
maskClosable
/>When to use
Use Tour to introduce a feature by highlighting a sequence of real DOM elements and explaining each step. Use Tooltip or Popover for one element, and Dialog or Modal for a blocking confirmation.
Import
import { Tour, resolveTarget, computeSpotlight, computeCardPosition, type Rect } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| steps* | TourStep[] | — | Guided steps described below. |
| open* | boolean | — | Controlled open state. |
| current* | number | — | Controlled zero-based step index. |
| maskClosable | boolean | false | Whether clicking the mask closes the tour. |
| spotlightPadding | number | 8 | Space around the target cutout in pixels. |
| spotlightRadius | number | 8 | Cutout corner radius. |
| gap | number | 12 | Distance between the target and card. |
| zIndex | number | 100 | Mask z-index. |
TourStep:
| Field | Type | Default | Description |
|---|---|---|---|
| target | (() => Element | null) | string | null | — | Target getter, CSS selector, or null for a centered card. Getters are most reliable for dynamic DOM. |
| title | ReactNode | — | Step title. |
| description | ReactNode | — | Step description. |
| placement | "top" | "bottom" | "left" | "right" | "bottom" | Preferred side, flipped when necessary; ignored without a target. |
Events
| Event | Type | Description |
|---|---|---|
| onChange | (current: number) => void | Reports Previous and Next navigation. |
| onClose | () => void | Called by Skip, Escape, or final completion when onFinish is absent. |
| onFinish | () => void | Optional final-step completion handler. |
Slots
| Slot | Type | Description |
|---|---|---|
| prevText | ReactNode | Previous-button override. Defaults to the active ConfigProvider locale. |
| nextText | ReactNode | Next-button override. Defaults to the active ConfigProvider locale. |
| skipText | ReactNode | Skip-button override. Defaults to the active ConfigProvider locale. |
| finishText | ReactNode | Finish-button override. Defaults to the active ConfigProvider locale. |
TourSteptitleanddescriptionare also ReactNode values, as shown above.
Example
const [open, setOpen] = useState(false);
const [current, setCurrent] = useState(0);
<Tour
open={open}
current={current}
onChange={setCurrent}
onClose={() => setOpen(false)}
steps={[
{ title: "Welcome", description: "Centered introduction" },
{ target: () => searchRef.current, title: "Global search", description: "Search across the app", placement: "bottom" },
{ target: "#new-btn", title: "Create an item", description: "Start here", placement: "left" },
]}
/>Usage guidelines
- Tour is fully controlled. Store both
openandcurrent; navigation only callsonChange. Failing to write current back leaves the tour on the same step. - Prefer a target getter over a selector when the target mounts dynamically.
- The fullscreen overlay portals to body. For visual verification, open the tour before capturing the screenshot.
- Navigation, close, dialog, and progress labels follow the nearest
ConfigProviderlocale (zhCNby default, orenUS). Explicit text props take precedence.
Related
Playground
Click the button below to start the novice guide.
const [open, setOpen] = useState(false);
const [current, setCurrent] = useState(0);
<Tour
open={open}
current={current}
onChange={setCurrent}
onClose={() => setOpen(false)}
steps={[
{ title: "Welcome", description: "Opening centered..." },
{ target: () => searchRef.current, title: "Global Search", description: "...", placement: "bottom" },
{ target: "#new-btn", title: "Create a new one", description: "...", placement: "left" },
]}
/>