Skip to main content
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Tour

Point each step at a control and show a card next to it. Next, Back, and the keyboard move through the tour.

Edit this page
Report a Bug
Open issues
View on GitHub
Copy Markdown

PreviewRenders elementIntermediateOct 6, 2026
Browser Support: CSS Anchor Positioning
Chrome 125+ Edge 125+ Firefox 147+ Safari 26+

Tour.Content positions against the activator with CSS Anchor Positioning. In browsers without position-area support it falls back to a viewport-edge position: fixed placement.

Installation

Install the plugin once. Tour reads that tour through useTour(). Registering steps, start, and next are documented on useTour.

main.ts
import { createApp } from 'vue'
import { createTourPlugin } from '@vuetify/v0'
import App from './App.vue'

const app = createApp(App)

app.use(createTourPlugin())

app.mount('#app')

A page that should run its own tour provides createTourContext instead of reading the plugin. The example below does that.

Usage

Wrap each target in Tour.Activator, and put that step’s card in its own Tour.Root.

Press Start.

Theme
Mode
Palettes
Accessibility
<script setup lang="ts">
  import { createTourContext, Tour } from '@vuetify/v0'

  const [, provideTour, tour] = createTourContext()

  provideTour()

  tour.steps.onboard([
    { id: 'search', placement: 'bottom' },
    { id: 'settings', placement: 'bottom' },
    { id: 'avatar', placement: 'left' },
  ])

  const isActive = tour.isActive
  const isComplete = tour.isComplete

  function onStart () {
    tour.start()
  }
</script>

<template>
  <div class="flex flex-col gap-4 w-full">
    <Tour.Keyboard />

    <Tour.Highlight blocking />

    <div class="flex items-center gap-3 px-3 py-2 rounded-lg bg-surface border border-divider">
      <Tour.Activator
        class="flex-1 rounded-md data-[state=active]:ring-2 data-[state=active]:ring-primary"
        :scroll="false"
        step="search"
      >
        <input
          class="w-full px-3 py-1.5 rounded-md text-sm bg-surface border border-divider text-on-surface"
          placeholder="Search…"
          type="search"
        >
      </Tour.Activator>

      <Tour.Activator
        class="rounded-md data-[state=active]:ring-2 data-[state=active]:ring-primary"
        :scroll="false"
        step="settings"
      >
        <button
          class="px-3 py-1.5 rounded-md text-sm border border-divider bg-surface text-on-surface hover:bg-surface-tint"
          type="button"
        >
          Settings
        </button>
      </Tour.Activator>

      <Tour.Activator
        class="rounded-full data-[state=active]:ring-2 data-[state=active]:ring-primary"
        :scroll="false"
        step="avatar"
      >
        <button
          class="size-8 rounded-full text-xs font-medium bg-primary text-on-primary"
          type="button"
        >
          AJ
        </button>
      </Tour.Activator>
    </div>

    <Tour.Root step="search">
      <Tour.Content
        class="flex flex-col gap-3 p-4 rounded-xl bg-surface border border-divider shadow-xl max-w-xs"
        placement="bottom"
      >
        <Tour.Title class="text-base font-semibold text-on-surface">
          Search
        </Tour.Title>

        <Tour.Description class="text-sm text-on-surface-variant">
          Search pages, docs, and people.
        </Tour.Description>

        <Tour.Progress class="text-xs text-on-surface-variant" />

        <div class="flex items-center justify-end gap-2">
          <Tour.Skip class="px-3 py-1.5 text-sm font-medium rounded-md text-on-surface-variant hover:bg-surface-tint">
            Skip
          </Tour.Skip>

          <Tour.Prev class="px-3 py-1.5 text-sm font-medium rounded-md border border-divider hover:bg-surface-tint data-[disabled]:opacity-50">
            Back
          </Tour.Prev>

          <Tour.Next
            v-slot="{ isLast }"
            class="px-3 py-1.5 text-sm font-medium rounded-md bg-primary text-on-primary data-[disabled]:opacity-50"
          >
            {{ isLast ? 'Done' : 'Next' }}
          </Tour.Next>
        </div>
      </Tour.Content>
    </Tour.Root>

    <Tour.Root step="settings">
      <Tour.Content
        class="flex flex-col gap-3 p-4 rounded-xl bg-surface border border-divider shadow-xl max-w-xs"
        placement="bottom"
      >
        <Tour.Title class="text-base font-semibold text-on-surface">
          Settings
        </Tour.Title>

        <Tour.Description class="text-sm text-on-surface-variant">
          Theme, locale, and account preferences.
        </Tour.Description>

        <Tour.Progress class="text-xs text-on-surface-variant" />

        <div class="flex items-center justify-end gap-2">
          <Tour.Skip class="px-3 py-1.5 text-sm font-medium rounded-md text-on-surface-variant hover:bg-surface-tint">
            Skip
          </Tour.Skip>

          <Tour.Prev class="px-3 py-1.5 text-sm font-medium rounded-md border border-divider hover:bg-surface-tint data-[disabled]:opacity-50">
            Back
          </Tour.Prev>

          <Tour.Next
            v-slot="{ isLast }"
            class="px-3 py-1.5 text-sm font-medium rounded-md bg-primary text-on-primary data-[disabled]:opacity-50"
          >
            {{ isLast ? 'Done' : 'Next' }}
          </Tour.Next>
        </div>
      </Tour.Content>
    </Tour.Root>

    <Tour.Root step="avatar">
      <Tour.Content
        class="flex flex-col gap-3 p-4 rounded-xl bg-surface border border-divider shadow-xl max-w-xs"
        placement="bottom"
      >
        <Tour.Title class="text-base font-semibold text-on-surface">
          Account
        </Tour.Title>

        <Tour.Description class="text-sm text-on-surface-variant">
          Open your profile, or sign out.
        </Tour.Description>

        <Tour.Progress class="text-xs text-on-surface-variant" />

        <div class="flex items-center justify-end gap-2">
          <Tour.Skip class="px-3 py-1.5 text-sm font-medium rounded-md text-on-surface-variant hover:bg-surface-tint">
            Skip
          </Tour.Skip>

          <Tour.Prev class="px-3 py-1.5 text-sm font-medium rounded-md border border-divider hover:bg-surface-tint data-[disabled]:opacity-50">
            Back
          </Tour.Prev>

          <Tour.Next
            v-slot="{ isLast }"
            class="px-3 py-1.5 text-sm font-medium rounded-md bg-primary text-on-primary data-[disabled]:opacity-50"
          >
            {{ isLast ? 'Done' : 'Next' }}
          </Tour.Next>
        </div>
      </Tour.Content>
    </Tour.Root>

    <div class="flex items-center gap-3">
      <button
        class="px-4 py-2 rounded-md text-sm font-medium bg-primary text-on-primary disabled:opacity-50"
        :disabled="isActive"
        type="button"
        @click="onStart"
      >
        Start tour
      </button>

      <p v-if="isComplete" class="text-sm text-on-surface-variant">
        Tour complete.
      </p>

      <p v-else-if="!isActive" class="text-sm text-on-surface-variant">
        Press Start.
      </p>
    </div>
  </div>
</template>

Anatomy

vue
<script setup lang="ts">
  import { Tour } from '@vuetify/v0'
</script>

<template>
  <Tour.Keyboard />

  <Tour.Highlight />

  <Tour.Root step="intro">
    <Tour.Activator step="intro" />

    <Tour.Content>
      <Tour.Title />

      <Tour.Description />

      <Tour.Progress />

      <Tour.Prev />

      <Tour.Next />

      <Tour.Skip />
    </Tour.Content>
  </Tour.Root>
</template>

Examples

Product walkthrough

A three-step walkthrough: search, settings, and the avatar. The instance is created once with createTourContext, provided to the page, and onboarded with three steps. Each Tour.Root is one step. It exposes that step’s isActive, next, prev, stop, and complete to its children. Activators wrap the controls. One Tour.Highlight with blocking paints the scrim, and Tour.Keyboard binds arrows, Enter, and Escape while the tour is active.

Content portals to the body once the step’s activator has an element, then anchors with CSS position-area (or a viewport-edge fallback). placement lives on Tour.Content. A ticket placement of top, bottom, left, right, or center overrides it. The avatar step sets placement: 'left' on the ticket so that step sits beside the control while the others keep Content’s bottom default. Next on the last step calls complete() instead of a no-op next(); Skip emits skip and then stop(), so the tour ends without isComplete. blocking swallows backdrop clicks and does not dismiss. Escape or Skip does.

Reach for this shape when the walkthrough targets elements that already exist in the page. Prefer createTour without the compound when you only need the sequencer and will paint chrome yourself. Filter the onboard list before start() if a step should not run on a given viewport; do not expect the factory to drop steps for you.

FileRole
useOnboarding.tsCreates the tour context, provides it, and onboards the three step tickets
OnboardingTour.vueChrome bar, activators, highlight, keyboard, and per-step Root / Content
product-tour.vueProvides the instance, the Start control, and the complete status

Press Start.

Theme
Mode
Palettes
Accessibility

Accessibility

Tour.Content renders a non-modal dialog (role="dialog", no aria-modal) labelled by Tour.Title and described by Tour.Description. The spotlight target stays in the page, so Tab is allowed to leave the card. Tour.Progress is a live region (role="status") announcing the localized step count. Highlight also exposes a tour-level status region so the step count is announced when Progress lives inside a step. Tour.Highlight’s scrim is aria-hidden. When the overlay is ready, focus moves to Content unless a field already has it. When the tour stops or completes, focus returns to the element that was focused before start(). blocking also marks the rest of the page inert. The card, the scrim, and the active target stay operable unless blockActivator is set.

ARIA Attributes

AttributeValueElement
roledialogContent
aria-labelledbyTitle element IDContent
aria-describedbyDescription element IDContent
rolestatusProgress
aria-labelLocalized previous / next / complete / skip stringPrev, Next, Skip
aria-disabledbooleanPrev, Next
aria-hiddentrueHighlight

Tour.Title and Tour.Description generate the IDs referenced by Content. Render them inside Tour.Content so the dialog has an accessible name and description. Prev is disabled on the first step; Next is disabled while the tour is unready.

Keyboard Navigation

KeyAction
ArrowRightAdvances; completes on the last step
ArrowLeftMoves to the previous step
EnterAdvances unless a control is focused; completes on the last step. Enter uses the same widget guard as the arrows.
EscapeStops the tour without completing
TabMoves focus. It is not trapped; the target stays reachable
EscapeStops the tour even when Tour.Keyboard is omitted. Content listens on its own

Tour.Keyboard is renderless and only binds arrows and Enter while isActive. Arrows do nothing when focus is inside a slider, tablist, listbox, radiogroup, menu, or <select>. Escape is also bound by Content, so a card still dismisses without Keyboard. Space on a focused Prev / Next / Skip activates that control natively.

FAQ

Discord
Need help? Join our community for support and discussions ↗
Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/