react-joyride
A React component and hook for building guided product tours with spotlight overlays and floating tooltips.
Repository Health
Technical Analysis
react-joyride lets you script step-by-step walkthroughs of a React app by pointing at existing DOM elements — no redesign of the UI required. Each step spotlights a target with an animated beacon, then opens a positioned tooltip with back/next/skip controls, driven by a small internal state machine that tracks tour status and per-step lifecycle separately.
Both a <Joyride> component and a useJoyride() hook expose the same engine, so teams can either drop in a fully rendered tour or wire the state into their own UI via the onEvent callback. Controlled and uncontrolled modes are both supported: uncontrolled tours auto-advance internally, while controlled tours pause at each step and let the parent decide when to move on — useful for tours gated on async data or route changes.
Under the hood it uses Floating UI for tooltip/beacon positioning (with automatic flip/shift and scroll-into-view), a focus trap for keyboard accessibility, and per-step before/after hooks with timeouts for async setup. The library ships as ESM/CJS with bundled types and works across React 16.8 through 19, including SSR frameworks like Next.js and Remix.
What You Get
- A
<Joyride>component and an equivalentuseJoyride()hook, both backed by the same tour engine and returning the same controls/state/events shape - Controlled mode (parent owns
stepIndex) and uncontrolled mode (internal auto-advance), selected simply by whetherstepIndexis passed - Floating UI-based positioning for tooltips and beacons with flip/shift/arrow middleware, plus automatic scroll-into-view for off-screen targets
- A single
onEvent(data, controls)callback surfacing a well-defined event stream (tour:start, step:before, beacon, tooltip, step:after, tour:end, error:target_not_found, etc.) - Per-step
before/afterasync hooks with configurable timeouts, so a step can fetch data or wait on a condition before its target is highlighted - Full customization points — custom Beacon/Tooltip/Arrow/Loader components, style overrides, and a
portalElementtarget for rendering inside a specific container
Common Use Cases
- Onboarding walkthroughs that introduce new users to a dashboard’s key features on first login
- Feature-announcement tours that point out newly shipped UI after a release
- Guided setup flows that walk a user through a multi-step configuration screen, waiting on async validation between steps
- In-app help overlays triggered from a “Take a tour” button rather than shown automatically
- Coordinating a tour with app state (e.g. pausing on a step until a modal opens) using controlled mode plus the onEvent callback
Under The Hood
Architecture
The engine is layered around a plain-object Store class (src/modules/store.ts) that holds frozen state snapshots and notifies subscribers on change; useTourEngine creates one store per component instance via useRef and subscribes through useSyncExternalStore, so React re-renders stay in sync with an update source that lives outside React’s own state. Above the store, useTourEngine delegates to five focused sub-hooks (useControls, useEventEmitter, usePropSync, useLifecycleEffect, useScrollEffect) that each own one concern — control methods, event emission, external prop syncing, state-machine progression, and scroll-to-target — rather than one large effect. useLifecycleEffect alone runs five separate effects to drive a documented two-dimensional state machine (overall tour status plus per-step lifecycle phase), which the project’s own docs/architecture.md diagrams in full. The public surface is intentionally thin: useJoyride wraps the engine, strips the internal positioned flag, and returns a TourRenderer element, while the <Joyride> component is just an SSR guard around that hook.
Tech Stack
Written in TypeScript and built with tsdown to dual ESM/CJS output with bundled .d.ts files, targeting React 16.8 through 19 as a peer dependency. Positioning is delegated to @floating-ui/react-dom (two independent useFloating() instances for tooltip and beacon), scrolling to the scroll/scrollparent packages, and prop merging/equality to @fastify/deepmerge and @gilbarbara/deep-equal. A handful of small @gilbarbara/* utility packages (hooks, types) supply shared helpers the same author maintains elsewhere. There’s no runtime state library or CSS framework dependency — styling is generated in src/styles.ts and injected directly.
Code Quality
Every module and hook under src/ has a matching spec file in test/ (store, step merging, DOM helpers, each hook individually), run under Vitest with coverage, plus a full Playwright e2e suite exercising real browser interactions and a separate website/ app used as the e2e fixture target. CI (pnpm validate) runs lint, typecheck, unit tests with coverage, @arethetypeswrong/cli type-validation, bundle size limits, and SonarCloud static analysis on every push, and the after step hook and event dispatch are explicitly wrapped in try/catch so consumer callback errors can’t crash the tour. Naming and typing are consistent and strict throughout, with no untyped any escape hatches observed in the core engine.
API Design
Offering both a component and a hook that share one engine (and one return shape) lets consumers pick between a drop-in tour or full control over rendering, with controlled/uncontrolled mode selected implicitly by whether stepIndex is passed rather than a separate prop. The single onEvent(data, controls) callback consolidates what other tour libraries often split across many individual callbacks, and passing controls alongside event data lets a handler call next()/stop() directly without holding a separate ref. Getting started requires only a steps array with target/content pairs, with every other option — hooks, custom components, floating behavior — layered on as opt-in.
Used by 8 apps in this directory
Flowsint
Automation · Developer Tools
A privacy-first, graph-based OSINT investigation platform with 30+ automated enrichers for mapping relationships between domains, IPs, people, and organizations.
Hexabot
AI Development · Automation
Build and run agentic workflows across channels with YAML, tools, and RAG
Label Studio
AI Development · Data Engineering
Label Studio is an open-source, multi-type data labeling platform that lets teams annotate images, text, audio, video, and time series data with a configurable XML-based UI and export annotations in formats ready for any ML framework.
Laudspeaker
Marketing · Automation
Open-source customer engagement platform for building visual, event-triggered messaging journeys across email, SMS, push, in-app, and webhooks.
LimeSurvey
Forms Surveys
The world's most flexible open-source survey platform with 900+ templates, conditional logic, 80+ languages, and full GDPR compliance for any scale.
Plasmic
CMS · Low Code Platforms · No Code Platforms
The open-source visual builder that lets teams design React apps and websites with drag-and-drop while integrating seamlessly with your codebase.
Teable
Databases · No Code Platforms
A no-code PostgreSQL database with spreadsheet UX, real-time collaboration, and native AI agents — built for teams that outgrow Airtable.
Unleash
Developer Tools · Devops · Ab Testing Experimentation
The open-source feature management platform that lets you ship code to production and control who sees it — without redeploying.