@tent-official/react-walkthrough
v1.1.95
Published
Lightweight React walkthrough/tour component with auto-positioning, dependency chains, and smooth animations
Maintainers
Readme
@tent-official/react-walkthrough
Lightweight React walkthrough/tour component with auto-positioning, dependency chains, and smooth animations.
Features
- SVG-based spotlight overlay with smooth animated transitions
- Auto-positioning popover (top/bottom/left/right) with viewport detection
- Dependency chains — start a walkthrough only after another completes
- Conditional start —
startWhenConditionprop to gate walkthrough on custom logic (e.g. data loaded) - Auto-cleanup on navigation — highlight is automatically removed when the component unmounts (e.g. browser back/forward)
- Customizable labels, colors, and layout
- Reset and replay walkthroughs without page refresh
- Portal-based rendering (works inside modals)
- Zero external dependencies (only React as peer dep)
Installation
npm install @tent-official/react-walkthrough
# or
yarn add @tent-official/react-walkthroughPeer Dependencies
Make sure these are installed in your project:
npm install react react-domQuick Start
import { useWalkthrough, WalkthroughOverlay } from "@tent-official/react-walkthrough";
function App() {
useWalkthrough({
name: "intro-tour",
delay: 500, // wait 500ms before highlighting the first step
steps: [
{
el: "welcome-title",
title: "Welcome!",
description: [{ description: "This is the main title of your app." }],
},
{
el: "nav-menu",
title: "Navigation",
description: [{ description: "Use this menu to navigate around." }],
position: "right",
},
],
});
return (
<div>
<h1 id="welcome-title">My App</h1>
<nav id="nav-menu">...</nav>
<WalkthroughOverlay />
</div>
);
}API
useWalkthrough(options)
Hook to register a walkthrough tour.
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| name | string | required | Unique name for this walkthrough |
| steps | IWalkthroughStep[] | required | Array of steps |
| storageSuffix | string | "" | Storage suffix for localStorage |
| dependsOn | string[] | [] | Walkthrough names that must complete first |
| startWhenCondition | boolean \| () => boolean | undefined | Additional condition that must be true for the walkthrough to start. Both dependsOn AND startWhenCondition must be satisfied. Example: startWhenCondition: list.length > 0 |
| onWalkthroughComplete | (name: string) => void | — | Callback when walkthrough completes (both finish and skip) |
| handleWhenLastStep | () => void | — | Callback fired only when the user clicks "Done" on the last step (not on skip). Useful for triggering navigation (e.g. router.push) |
| isShowSkip | boolean | true | Show skip button |
| isShowPrev | boolean | true | Show back button (can be overridden per step) |
| isShowStep | boolean | true | Show step counter e.g. "1/3" (can be overridden per step) |
| nextLabel | string | "Next" | Next button label (can be overridden per step) |
| prevLabel | string | "Back" | Previous button label (can be overridden per step) |
| skipLabel | string | "Skip" | Skip button label (can be overridden per step) |
| delay | number | 0 | Delay in ms before highlighting the first step. During delay, a dark overlay is shown |
| doneLabel | string | "Done" | Done button label (last step) |
| containerElement | string | — | DOM element ID of the scrollable container (e.g. a drawer). When provided, scrolls within this container instead of the window |
| animationSpeed | number | 350 | Base animation speed (ms) used to calculate transition duration from distance |
| displayTotal | number | — | Override the total step count displayed in the step counter (display-only, does not affect logic) |
| displayStepOffset | number | 0 | Offset added to the displayed current step number (display-only, does not affect logic). e.g. displayStepOffset: 3 with 3 real steps shows 4/total → 5/total → 6/total |
| isClearLastPositionOnEnd | boolean | false | When true, clears the saved last highlight position when the tour ends. This prevents the next tour from animating its highlight from this tour's last position |
Returns: { start: () => void }
IWalkthroughStep
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| el | string | required | DOM element ID to highlight (with or without # prefix) |
| title | string | — | Popover title |
| description | IStepDescription[] | required | Description blocks |
| position | "top" \| "bottom" \| "left" \| "right" | auto | Preferred popover position |
| padding | number | 8 | Padding around highlighted element (px) |
| borderRadius | number | 10 | Border radius of highlight cutout (px) |
| width | number \| string | "auto" | Popover width |
| height | number \| string | — | Popover height |
| triggerElOnNext | string \| boolean | — | When set, clicking "Next" triggers a click on an element. If a string (element ID), clicks that element; if true, clicks the step's own target element |
| isShowPrev | boolean | — | Override tour-level isShowPrev for this step |
| isShowStep | boolean | — | Override tour-level isShowStep for this step |
| nextLabel | string | — | Override tour-level nextLabel for this step |
| prevLabel | string | — | Override tour-level prevLabel for this step |
| skipLabel | string | — | Override tour-level skipLabel for this step |
IStepDescription
| Property | Type | Description |
| --- | --- | --- |
| title | string | Optional label for this block |
| description | ReactNode | Description content |
| direction | "row" \| "column" | Layout direction |
<WalkthroughOverlay />
Place this component once at the root of your app. It renders via React Portal into document.body.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| $popoverPadding | number | 12 | Inner padding of popover (px) |
| $popoverBorderRadius | number | 8 | Border radius of popover (px) |
| $popoverGap | number | 12 | Gap between highlight and popover (px) |
| $popoverMinWidth | number | 275 | Minimum width of the popover (px) |
| $animationSpeed | number | 350 | Base animation speed (ms). Actual duration is calculated from distance — shorter distance = faster, longer distance = slower. Lower value = faster overall, higher = slower overall |
| nextColor | string | "#000" | Custom next button color |
| prevColor | string | — | Custom previous button color |
| skipColor | string | — | Custom skip button color |
resetWalkthrough(options?)
Reset walkthroughs so they can be replayed without page refresh.
import { resetWalkthrough } from "@tent-official/react-walkthrough";
resetWalkthrough({
storageSuffix: "my-app",
walkthroughList: ["intro-tour", "feature-tour"],
});| Option | Type | Default | Description |
| --- | --- | --- | --- |
| storageSuffix | string | "" | Storage suffix |
| walkthroughList | string[] | [] | Names of walkthroughs to reset |
getCompletedWalkthroughs(options?)
Get a list of all completed walkthrough names. Returns an array of walkthrough name strings that have been marked as done.
import { getCompletedWalkthroughs } from "@tent-official/react-walkthrough";
const list = getCompletedWalkthroughs({ storageSuffix: "my-app" });
// => ["intro-tour", "feature-tour"]| Option | Type | Default | Description |
| --- | --- | --- | --- |
| storageSuffix | string | "" | Storage suffix matching the one used in useWalkthrough |
Dependency Chains
You can create sequential walkthroughs using dependsOn:
// This runs first
useWalkthrough({
name: "intro-tour",
storageSuffix: "my-app",
steps: [/* ... */],
});
// This starts automatically after intro-tour completes
useWalkthrough({
name: "advanced-tour",
storageSuffix: "my-app",
dependsOn: ["intro-tour"],
steps: [/* ... */],
});Conditional Start
Use startWhenCondition to gate a walkthrough on custom logic. The walkthrough will only start when both dependsOn (if any) and startWhenCondition are satisfied:
function Dashboard({ items }) {
// Won't start until items are loaded AND intro-tour is done
useWalkthrough({
name: "items-tour",
storageSuffix: "my-app",
dependsOn: ["intro-tour"],
startWhenCondition: items.length > 0,
steps: [/* ... */],
});
// Also accepts a function
useWalkthrough({
name: "profile-tour",
startWhenCondition: () => document.getElementById("profile-btn") !== null,
steps: [/* ... */],
});
return <div>...</div>;
}When
startWhenConditionisundefined(default), onlydependsOnis checked — backward compatible.
Auto-Cleanup on Navigation
The walkthrough highlight is automatically removed when the component that registered it unmounts. This means:
- Pressing browser back/forward properly clears the overlay
- Route changes (e.g. React Router, Next.js) clean up automatically
- No stale highlights left on screen after navigation
No extra configuration needed — this works out of the box.
Keyboard Blocking
While a walkthrough is active, the following keyboard keys are automatically blocked to prevent users from accidentally interacting with highlighted elements behind the overlay:
| Key | Reason |
| --- | --- |
| Enter | Prevents triggering click on focused elements |
| Space | Prevents triggering click on buttons/checkboxes |
| Escape | Prevents closing dialogs/dropdowns being highlighted |
| Backspace | Prevents browser back navigation or input deletion |
| Delete | Prevents deleting content in inputs |
| Tab | Prevents moving focus to elements behind the overlay |
Walkthrough buttons (Next / Back / Skip / Done) remain fully functional — only keys targeting elements outside the walkthrough are blocked.
Auto-Positioning
The popover automatically positions itself to stay within the viewport:
- If
positionis specified, it tries that position first - If it doesn't fit, it falls back to: bottom → top → right → left
- If nothing fits perfectly, it picks the direction with the most available space
The popover also waits for the target element to be scrolled into view before appearing.
License
MIT
