Skip to Content
PatternsOnboarding tour (Joyride)

Onboarding tour (Joyride)

A guided onboarding tour system powered by React Joyride  with a custom Apollo Vertex–styled tooltip, welcome modals, and step-by-step navigation. Consumers define their own tour steps and point them at app-specific elements via CSS selectors.

Welcome modal

Use the type: 'modal' step to show a full-screen centered modal — ideal for welcome screens, success states, or intro steps.

Step popover

The default step type renders a card-style popover with progress bars, navigation, and an optional tip section.

Features

  • Spotlight overlay: Highlights target elements with a cutout in a dimmed overlay (via React Joyride)
  • Popover cards: Step-by-step cards with progress bars, title, body, tips, and navigation
  • Welcome modal: Full-screen modal variant for intro/success steps with optional images
  • CSS selector targeting: Target elements by CSS selector — no wrapper components needed
  • Smooth transitions: Native React rendering of the tooltip animates cleanly between steps
  • Conditional steps: Gate steps on conditions with type-safe generics
  • Keyboard navigation: Escape to skip the tour, focus trap via React Joyride
  • Persistence: Tracks completed tours in localStorage so they don’t repeat
  • Dark mode: Automatically adapts overlay color to the current theme

Installation

npx shadcn@latest add @uipath/onboarding-tour-joyride

Usage

1. Define your tour

import type { TourDefinition } from "@/components/ui/onboarding-tour-joyride"; const myTour: TourDefinition = { id: "my-onboarding-tour", steps: [ { id: "welcome", type: "modal", title: "Welcome!", body: "Let us show you around your new workspace.", tip: "This tour will only show once.", nextLabel: "Let's go", image: "/assets/welcome.png", // optional }, { id: "sidebar", selector: "[data-sidebar]", title: "Navigation", body: "Use the sidebar to move between sections.", placement: "right", }, { id: "search", selector: "#search-bar", title: "Quick search", body: "Find anything across your workspace.", placement: "bottom", tip: "Try pressing Cmd+K for a shortcut.", }, ], };

2. Set up the provider

Wrap your app (or a section of it) with OnboardingTourProvider:

import { OnboardingTourProvider, } from "@/components/ui/onboarding-tour-joyride"; function App() { return ( <OnboardingTourProvider tours={[myTour]}> <YourApp /> </OnboardingTourProvider> ); }

3. Start the tour

import { useOnboardingTour, isTourCompleted } from "@/components/ui/onboarding-tour-joyride"; function WelcomeBanner() { const { startTour } = useOnboardingTour(); if (isTourCompleted("my-onboarding-tour")) return null; return ( <button onClick={() => startTour("my-onboarding-tour")}> Take a tour </button> ); }

Type-safe conditional steps

Use generics to make condition keys type-safe. The waitFor property gates steps on conditions set via setCondition:

type MyConditions = "profile-saved" | "data-loaded"; const tour: TourDefinition<MyConditions> = { id: "conditional-tour", steps: [ { id: "intro", selector: "#intro-section", title: "Getting started", body: "First, let's set up your profile.", placement: "right", }, { id: "results", selector: "#results-panel", title: "Your results", body: "Here's what we found.", placement: "left", waitFor: "profile-saved", // type-safe — must be a MyConditions key }, ], }; // In your component: const { setCondition } = useOnboardingTour<MyConditions>(); async function handleSaveProfile(formData: FormData) { await saveProfile(formData); setCondition("profile-saved", true); // type-safe }

Resetting a tour

Import resetTourState to clear completion state so a tour can be shown again:

import { resetTourState } from "@/components/ui/onboarding-tour-joyride"; function SettingsPage() { return ( <button onClick={() => resetTourState("my-onboarding-tour")}> Replay onboarding tour </button> ); }

Step types

PropertyTypeDescription
idstringUnique step identifier
selectorstringCSS selector for the target element. Omit for centered/modal steps.
titlestringStep title
bodyReactNodeStep body content
tipstringOptional tip shown with a lightbulb icon
placement'top' | 'bottom' | 'left' | 'right'Popover position relative to target
type'popover' | 'modal'Render as popover card (default) or welcome modal
imagestringImage URL for modal-type steps
waitForTConditionType-safe condition key that must be true before step can be reached
nextLabelstringCustom label for the Next button
onEnter() => voidCallback when step becomes active

Components

ComponentDescription
OnboardingTourProviderContext provider — wrap your app with this. Accepts tours and optional initialConditions.
OnboardingTourJoyridePopoverJoyride tooltipComponent that renders the step card.
OnboardingTourJoyridePopoverCardStandalone popover card (useful for previews or custom renderers).
OnboardingTourJoyrideWelcomeModalThe welcome/success modal (portal + focus trap via Radix Dialog).
OnboardingTourJoyrideWelcomeModalCardStandalone modal card (useful for previews).

API

ExportDescription
useOnboardingTour<TCondition>()Access tour actions: startTour, setCondition. Must be inside OnboardingTourProvider.
isTourCompleted(tourId)Check whether a tour has been completed.
markTourCompleted(tourId)Manually mark a tour as completed.
resetTourState(tourId)Clear completion state so the tour can be shown again.
Last updated on