Build User Walkthroughs with Tour Component for Shadcn/UI

A tour component that extends shadcn/ui with onboarding walkthroughs. Highlight elements and guide users through your applications with custom styling.

Tour is a lightweight UI component that manages onboarding flows and guided walkthroughs within applications built on shadcn/ui.

It allows you to build customizable, interactive, step-by-step guides by targeting specific DOM elements through data attributes and displaying contextual information in popover cards.

Features

🎯 Element Targeting: Attach tours to specific DOM elements using data attributes that the system automatically locates and highlights.

🧭 Multi-Tour Support: Define separate tour flows for different features and trigger them independently through unique identifiers.

📍 Popover Positioning: Control card placement with side, alignment, and offset parameters inherited from Radix UI primitives.

🔄 Route Navigation: Link tour steps to specific routes so the component handles page transitions during the walkthrough.

⚙️ Custom Styling: Override default appearances per step or globally through className properties and CSS modifications.

🎛️ Context API Control: Start and close tours programmatically from any component using the provided React hook.

See It In Action

Use Cases

  • Application Onboarding: Walk new users through your dashboard layout and primary actions when they first log in.
  • Feature Announcements: Highlight newly released functionality with a dedicated tour that appears after deployment.
  • Settings Walkthroughs: Guide users through configuration panels when they access complex settings for the first time.
  • Multi-Page Workflows: Connect tour steps across different routes to demonstrate processes that span multiple screens.
  • Interactive Documentation: Create contextual help systems that highlight parts of a complex dashboard.
  • Tutorial Creation: Build sequential, in-app tutorials for specific user tasks or configurations.

How to Use It

1. Install the component into your existing shadcn/ui project using the CLI.

# pnpm
pnpm dlx shadcn@latest add https://onboarding-tour.vercel.app/r/tour.json
# npm
npx shadcn@latest add https://onboarding-tour.vercel.app/r/tour.json
# yarn
yarn shadcn@latest add https://onboarding-tour.vercel.app/r/tour.json
# bun
bunx --bun shadcn@latest add https://onboarding-tour.vercel.app/r/tour.json

2. Wrap your application root with TourProvider and pass an array of tour definitions. Each tour contains an id and a steps array.

import { type Tour, TourProvider } from "@/components/ui/tour"
const tours = [
  {
    id: "onboarding",
    steps: [
      {
        id: "welcome",
        title: "Welcome",
        content: "This tour shows you the main features.",
      },
      {
        id: "dashboard",
        title: "Dashboard",
        content: "View your analytics here.",
      },
    ],
  },
] satisfies Tour[]
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <TourProvider tours={tours}>
          {children}
        </TourProvider>
      </body>
    </html>
  )
}

3. Add data attributes to the elements you want to highlight. The tour matches step ids to these attributes and displays the popover near the target.

export function DashboardPage() {
  return (
    <div>
      <div data-tour-step-id="welcome">
        <h1>Welcome to the App</h1>
      </div>
      <div data-tour-step-id="dashboard">
        <p>Your analytics dashboard</p>
      </div>
    </div>
  )
}

4. Trigger tours from any component using the useTour hook. Call the start method with the tour id.

"use client"
import { Button } from "@/components/ui/button"
import { useTour } from "@/components/ui/tour"
export function StartButton() {
  const tour = useTour()
  return (
    <Button onClick={() => tour.start("onboarding")}>
      Start Tour
    </Button>
  )
}

5. Customize step appearance with positioning props. These control where the popover renders relative to the target element.

const customTour = [
  {
    id: "features",
    steps: [
      {
        id: "sidebar",
        title: "Navigation",
        content: "Access different sections here.",
        side: "right",
        sideOffset: 20,
        align: "start",
        className: "w-80",
      },
    ],
  },
] satisfies Tour[]

6. Link steps to routes when your tour spans multiple pages. The component handles navigation automatically when users click next or previous.

{
  id: "settings-tour",
  steps: [
    {
      id: "profile",
      title: "Profile Settings",
      content: "Update your information.",
      nextRoute: "/settings/security",
    },
    {
      id: "security",
      title: "Security Options",
      content: "Manage authentication.",
      previousRoute: "/settings/profile",
    },
  ],
}

7. Override button labels for specific steps when the default text does not fit your context.

{
  id: "complete",
  title: "Finished",
  content: "You completed the tour.",
  nextLabel: "Get Started",
  previousLabel: "Review",
}

API Reference

TourProvider Props

PropTypeDescription
toursTour[]Array of tour configurations that define all available tours in the application.
childrenReact.ReactNodeApplication content that receives tour context.

Tour Configuration

PropertyTypeDescription
idstringUnique identifier used to start the tour programmatically.
stepsStep[]Ordered array of step objects that define the tour sequence.

Step Configuration

PropertyTypeDescription
idstringUnique identifier that matches the data-tour-step-id attribute on target elements.
titleReact.ReactNodeHeading displayed at the top of the tour card.
contentReact.ReactNodeMain text or components shown in the card body.
side“top” | “bottom” | “left” | “right”Preferred placement of the popover relative to the target element.
sideOffsetnumberDistance in pixels between the popover and the target element.
align“start” | “center” | “end”Alignment of the popover along the chosen side.
alignOffsetnumberPixel offset from the start or end alignment position.
classNamestringCSS classes applied to the popover container for custom styling.
nextRoutestringRoute path to navigate to when the next button is clicked.
previousRoutestringRoute path to navigate to when the previous button is clicked.
nextLabelReact.ReactNodeCustom content for the next button. Defaults to “Next” or “Finish” on the last step.
previousLabelReact.ReactNodeCustom content for the previous button. Defaults to “Previous”.

useTour Hook

MethodTypeDescription
start(tourId: string) => voidInitiates the tour matching the provided id.
close() => voidTerminates the currently active tour.

Related Resources

  • Radix UI Popover: Primitive component that handles popover positioning and accessibility logic.
  • shadcn/ui: Component collection that Tour extends for consistent design patterns.
  • Intro.js: Alternative onboarding library with different API patterns and vanilla JavaScript support.
  • React Joyride: Tour component with beacon animations and standalone React implementation.
  • Best Guide JS Plugins: 10 Best Tour Plugins To Guide Visitors Through Your App.

FAQs

Q: Can I target the same element in multiple tour steps?
A: Yes. Pass an array of step ids to the data-tour-step-id attribute. The element will appear in each specified step.

Q: How do I customize the tour appearance globally instead of per step?
A: Modify the tour.tsx component file directly. Apply default styling changes to the popover wrapper or card components.

Q: Does the tour work with client-side routing libraries?
A: Yes. The nextRoute and previousRoute properties integrate with Next.js routing. The component triggers navigation when users advance through steps.

Q: Can I close a tour programmatically without user interaction?
A: Yes. Call the close method from the useTour hook. This terminates the active tour from any component.

Q: What happens if a target element does not exist on the page?
A: The tour skips that step automatically. Make sure elements with matching data attributes render before starting the tour.

tonghohin

tonghohin

Leave a Reply

Your email address will not be published. Required fields are marked *