Skip to main content

react-joyride

Guide for implementing, configuring, and debugging React Joyride v3 guided tours. Use this skill whenever the user mentions joyride, guided tour, onboarding tour, walkthrough, tooltip tour, step-by-step guide, product tour, or wants to highlight UI elements sequentially. Also use when debugging tour issues like tooltips not appearing, targets not found, or controlled mode problems. This skill covers the useJoyride hook, Joyride component, step configuration, events, controls, custom components, and styling.

Source facts

Repository
gilbarbara/react-joyride
Last source activity
March 23, 2026 at 00:04
Detected SKILL.md language
English
Stars
7,874
Forks
595

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
5 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
react-joyride
description
Guide for implementing, configuring, and debugging React Joyride v3 guided tours. Use this skill whenever the user mentions joyride, guided tour, onboarding tour, walkthrough, tooltip tour, step-by-step guide, product tour, or wants to highlight UI elements sequentially. Also use when debugging tour issues like tooltips not appearing, targets not found, or controlled mode problems. This skill covers the useJoyride hook, Joyride component, step configuration, events, controls, custom components, and styling.
license
MIT
metadata
{"author":"gilbarbara","version":"3.0.0"}
# React Joyride v3 Create guided tours in React apps. Two public APIs: the `useJoyride()` hook (recommended) and the `<Joyride>` component. Online docs: https://v3.react-joyride.com ## Quick Start ### Using the hook (recommended) ```tsx import { useJoyride, STATUS, Status } from 'react-joyride'; function App() { const { Tour } = useJoyride({ continuous: true, run: true, steps: [ { target: '.my-element', content: 'This is the first step', title: 'Welcome' }, { target: '#sidebar', content: 'Navigate here', placement: 'right' }, ], onEvent: (data) => { if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) { // Tour ended } }, }); return <div>{Tour}{/* rest of app */}</div>; } ``` ### Using the component ```tsx import { Joyride, STATUS, Status } from 'react-joyride'; function App() { return ( <Joyride continuous run={true} steps={[ { target: '.my-element', content: 'First step' }, { target: '#sidebar', content: 'Second step' }, ]} onEvent={(data) => { if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) { // Tour ended } }} /> ); } ``` The hook returns `{ controls, failures, on, state, step, Tour }`. Render `Tour` in your JSX. Docs: https://v3.react-joyride.com/docs/getting-started ## Core Concepts The tour has two state dimensions: **Tour Status**: `idle -> ready -> waiting -> running <-> paused -> finished | skipped` - `idle`: No steps loaded - `ready`: Steps loaded, waiting for `run: true` - `waiting`: `run=true` but steps loading async (transitions to running when steps arrive) - `running`: Tour active - `paused`: Tour paused (controlled mode at COMPLETE, or `stop()` called) - `finished` / `skipped`: Tour ended **Step Lifecycle** (per step): `init -> ready -> beacon_before -> beacon -> tooltip_before -> tooltip -> complete` - `*_before` phases: scrolling and positioning happen here - `beacon`: Pulsing indicator shown (skipped when `continuous` + navigating, `skipBeacon`, or `placement: 'center'`) - `tooltip`: The tooltip is visible and interactive Docs: https://v3.react-joyride.com/docs/how-it-works ## Step Configuration Each step requires `target` and `content`. All other fields are optional. ```tsx { target: '.my-element', // CSS selector, HTMLElement, React ref, or () => HTMLElement content: 'Step body text', // ReactNode title: 'Optional title', // ReactNode placement: 'bottom', // Default. Also: top, left, right, *-start, *-end, auto, center id: 'unique-id', // Optional identifier data: { custom: 'data' }, // Attached to event callbacks } ``` ### Target types ```tsx // CSS selector { target: '.sidebar-nav' } // HTMLElement { target: document.getElementById('my-el') } // React ref const ref = useRef(null); { target: ref } // Function (evaluated each lifecycle) { target: () => document.querySelector('.dynamic-element') } ``` ### Common step options (override per-step) | Option | Default | Description | |--------|---------|-------------| | `placement` | `'bottom'` | Tooltip position. Use `'center'` for modal-style (requires `target: 'body'`) | | `skipBeacon` | `false` | Skip beacon, show tooltip directly | | `buttons` | `['back','close','primary']` | Buttons in tooltip. Add `'skip'` for skip button | | `hideOverlay` | `false` | Don't show dark overlay | | `blockTargetInteraction` | `false` | Block clicks on highlighted element | | `before` | - | `(data) => Promise<void>` — async hook before step shows | | `after` | - | `(data) => void` — fire-and-forget hook after step completes | | `skipScroll` | `false` | Don't scroll to target | | `scrollTarget` | - | Scroll to this element instead of `target` | | `spotlightTarget` | - | Highlight this element instead of `target` | | `spotlightPadding` | `10` | Padding around spotlight. Number or `{ top, right, bottom, left }` | | `targetWaitTimeout` | `1000` | ms to wait for target to appear. `0` = no waiting | | `beforeTimeout` | `5000` | ms to wait for `before` hook. `0` = no timeout | All `Options` fields can be set globally via `options` prop or per-step. Per-step values override global. Docs: https://v3.react-joyride.com/docs/step | https://v3.react-joyride.com/docs/props/options ## Uncontrolled vs Controlled ### Uncontrolled (default — strongly preferred) The tour manages step navigation internally. This is the right choice for most use cases. **The library handles async transitions for you.** If a step needs to wait for a UI change (dropdown opening, data loading, animation), use `before` hooks — the tour waits for the promise to resolve before showing the step. If a target element isn't in the DOM yet, `targetWaitTimeout` (default: 1000ms) handles polling for it. You do NOT need controlled mode for these cases. ```tsx const { Tour } = useJoyride({ continuous: true, run: isRunning, steps: [ { target: '.nav', content: 'Navigation' }, { target: '.dropdown-item', content: 'Inside the dropdown', before: () => { // Open dropdown and wait for animation — tour waits automatically openDropdown(); return new Promise(resolve => setTimeout(resolve, 300)); }, after: () => closeDropdown(), // Clean up after step (fire-and-forget) }, { target: '.main-content', content: 'Main content' }, ], onEvent: (data) => { if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) { setIsRunning(false); } }, }); ``` ### Controlled (with `stepIndex`) — use sparingly Only use controlled mode when the parent genuinely needs to manage the step index externally (e.g., syncing with URL params, external state machines, or complex multi-component coordination that `before`/`after` hooks can't handle). ```tsx const [stepIndex, setStepIndex] = useState(0); const [run, setRun] = useState(true); const { Tour } = useJoyride({ continuous: true, run, stepIndex, // This makes it controlled steps, onEvent: (data) => { const { action, index, status, type } = data; if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(status)) { setRun(false); return; } if (type === 'step:after' || type === 'error:target_not_found') { setStepIndex(index + (action === 'prev' ? -1 : 1)); } }, }); ``` **Controlled mode rules:** - `go()` and `reset()` are disabled (logged warning) - You must update `stepIndex` in response to events - The tour pauses at COMPLETE — you must advance it - Prefer uncontrolled mode with `before`/`after` hooks unless you have a strong reason for external index management ## Event System ### `onEvent` callback ```tsx onEvent: (data: EventData, controls: Controls) => void ``` The `data` object contains the full tour state plus event-specific fields. The `controls` object lets you programmatically control the tour. ### Event types (in order per step) | Event | When | |-------|------| | `tour:start` | Tour begins | | `step:before_hook` | `before` hook is called | | `step:before` | Target found, step about to render | | `scroll:start` | Scrolling to target | | `scroll:end` | Scroll complete | | `beacon` | Beacon shown | | `tooltip` | Tooltip shown | | `step:after` | User navigated (next/prev/close/skip) | | `step:after_hook` | `after` hook called | | `tour:end` | Tour finished or skipped | | `tour:status` | Status changed (on stop/reset) | | `error:target_not_found` | Target element not found | | `error` | Generic error | ### Event subscription with `on()` ```tsx const { on, Tour } = useJoyride({ ... }); useEffect(() => { const unsubscribe = on('tooltip', (data, controls) => { analytics.track('tour_step_viewed', { step: data.index }); }); return unsubscribe; }, [on]); ``` Docs: https://v3.react-joyride.com/docs/events ## Controls Available via `useJoyride()` return value or `onEvent` second argument: | Method | Description | |--------|-------------| | `next()` | Advance to next step | | `prev()` | Go to previous step | | `close(origin?)` | Close current step, advance | | `skip(origin?)` | Skip the tour entirely | | `start(index?)` | Start the tour | | `stop(advance?)` | Stop (pause) the tour | | `go(index)` | Jump to step (uncontrolled only) | | `reset(restart?)` | Reset tour (uncontrolled only) | | `open()` | Open tooltip for current step | | `info()` | Get current state | Docs: https://v3.react-joyride.com/docs/hook ## Styling & Theming Three layers of customization (from simple to full control): ### 1. Color options (simplest) ```tsx options: { primaryColor: '#1976d2', // Buttons and beacon backgroundColor: '#1a1a2e', // Tooltip background textColor: '#ffffff', // Tooltip text overlayColor: 'rgba(0,0,0,0.7)', // Overlay backdrop arrowColor: '#1a1a2e', // Arrow (match background) } ``` ### 2. Styles override ```tsx styles: { tooltip: { borderRadius: 12 }, buttonPrimary: { backgroundColor: '#1976d2' }, buttonBack: { color: '#666' }, spotlight: { borderRadius: 8 }, } ``` Style keys: `arrow`, `beacon`, `beaconInner`, `beaconOuter`, `beaconWrapper`, `buttonBack`, `buttonClose`, `buttonPrimary`, `buttonSkip`, `floater`, `loader`, `overlay`, `tooltip`, `tooltipContainer`, `tooltipContent`, `tooltipFooter`, `tooltipFooterSpacer`, `tooltipTitle` ### 3. Custom components (full control) See next section. Docs: https://v3.react-joyride.com/docs/props/styles ## Custom Components Replace any UI component via props. Each receives render props with step data and button handlers. ### Custom Tooltip ```tsx import type { TooltipRenderProps } from 'react-joyride'; function MyTooltip({ backProps, index, primaryProps, size, skipProps, step, tooltipProps }: TooltipRenderProps) { return ( <div {...tooltipProps} style={{ background: '#fff', padding: 16, borderRadius: 8, width: step.width }}> {step.title && <h3>{step.title}</h3>} <div>{step.content}</div> <div> {index > 0 && <button {...backProps}>Back</button>} <button {...primaryProps}>Next</button> </div> </div> ); } // Usage <Joyride tooltipComponent={MyTooltip} ... />
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub