UILab

Tour

Guided product tour that spotlights elements on the page and walks users through them step by step.

Welcome to Your Dashboard

Take a quick tour to explore key features

Analytics

Track your performance metrics

Projects

Manage your active projects

Team

Collaborate with teammates

Introduction

Tour dims the page, cuts a spotlight around a target element, and anchors a step card next to it. Steps are positioned with Floating UI, and the buttons come from uilab-core.

Installation

Install via following command:

Reminder

Following CLI command will install @base-ui/react, @base-ui/utils and @floating-ui/react-dom to your project.

npx shadcn add @uilab-core/tour

Layout

Import the parts and compose them together.

import {
  Tour,
  TourArrow,
  TourClose,
  TourDescription,
  TourFooter,
  TourHeader,
  TourNext,
  TourPortal,
  TourPrev,
  TourSkip,
  TourSpotlight,
  TourSpotlightRing,
  TourStep,
  TourStepCounter,
  TourTitle,
} from "@/components/ui/tour"

<Tour>
  <TourPortal>
    <TourSpotlight />
    <TourSpotlightRing />
    <TourStep target="#element">
      <TourArrow />
      <TourClose />
      <TourHeader>
        <TourTitle />
        <TourDescription />
      </TourHeader>
      <TourFooter>
        <TourStepCounter />
        <TourPrev />
        <TourNext />
        <TourSkip />
      </TourFooter>
    </TourStep>
  </TourPortal>
</Tour>

target takes a CSS selector, a ref, or an element. Pass stepFooter to Tour to share one footer across every step instead of repeating it.

Examples

Controlled

Control the open state and the current step from outside the tour with value and onValueChange.

Controlled Tour

Step 1

First step in our controlled tour

Step 2

Second step with external controls

Custom spotlight styling

Style the highlighted area with TourSpotlightRing:

<TourSpotlightRing className="rounded-lg border-2 border-primary" />
<TourSpotlightRing className="rounded-xl ring-2 ring-blue-500 ring-offset-2" />
<TourSpotlightRing className="rounded-lg shadow-lg shadow-primary/50" />
<TourSpotlightRing className="animate-pulse rounded-lg border-2 border-primary" />

Global offsets

Set default spacing on Tour and override it per step:

<Tour open={open} onOpenChange={setOpen} sideOffset={16} alignOffset={0}>
  <TourPortal>
    <TourSpotlight />
    <TourSpotlightRing />
    {/* Uses sideOffset={16} */}
    <TourStep target="#step-1" side="bottom">
      <TourHeader>
        <TourTitle>Step 1</TourTitle>
      </TourHeader>
    </TourStep>
    {/* Overrides with sideOffset={32} */}
    <TourStep target="#step-2" side="top" sideOffset={32}>
      <TourHeader>
        <TourTitle>Step 2 - Larger gap</TourTitle>
      </TourHeader>
    </TourStep>
  </TourPortal>
</Tour>

API Reference

Tour

PropTypeDescription
open / defaultOpen / onOpenChangebooleanControlled or uncontrolled open state.
value / defaultValue / onValueChangenumberControlled or uncontrolled current step index.
onComplete() => voidCalled when the last step is finished.
onSkip() => voidCalled when the tour is skipped.
onEscapeKeyDown(event) => voidCalled on Escape. Closes the tour unless prevented.
onPointerDownOutside / onInteractOutside(event) => voidCalled on interaction outside the step.
onOpenAutoFocus / onCloseAutoFocus(event) => voidFocus management hooks.
dir"ltr" | "rtl"Reading direction.
sideOffset / alignOffsetnumberDefault offsets for every step.
spotlightPaddingnumberPadding around the spotlight cutout.
autoScroll / scrollBehavior / scrollOffsetScroll the target into view on each step.
dismissiblebooleanWhether outside clicks and Escape close the tour.
modalbooleanTrap focus and block interaction with the page.
stepFooterReactElementFooter rendered in every step.

TourStep

PropTypeDescription
targetstring | RefObject<HTMLElement> | HTMLElementElement to spotlight.
side"top" | "right" | "bottom" | "left"Side of the target to place the step.
align"start" | "center" | "end"Alignment against the target.
sideOffset / alignOffsetnumberOverrides the tour defaults.
collisionBoundary / collisionPadding / avoidCollisionsFloating UI collision handling.
arrowPaddingnumberPadding between the arrow and the card edges.
sticky"partial" | "always"Keep the step attached while scrolling.
hideWhenDetachedbooleanHide the step when the target is clipped.
requiredbooleanUser can't skip or close on this step.
forceMountbooleanKeep the step mounted when inactive.
onStepEnter / onStepLeave() => voidStep lifecycle callbacks.

TourSpotlight and TourSpotlightRing expose data-state="open" | "closed". TourStep exposes data-side and data-align.

Accessibility

KeyDescription
EscapeTriggers onEscapeKeyDown. Closes the tour unless prevented.
Tab / Shift+TabMoves focus between focusable elements in the step.
Enter / SpaceActivates the focused button (next, previous, skip or close).

Credits

Ported from Dice UI Tour.

On this page