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

useTour

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

PreviewIntermediateOct 6, 2026

Sequence a guided tour. Register each step, point it at a target when it opens, and move on when that step is ready.

Installation

Install the plugin once. Call useTour() during component setup. It returns that same tour.

ts
import { createTourPlugin } from '@vuetify/v0'

app.use(createTourPlugin())

A subtree that should run a different tour provides createTourContext instead. That provide shadows the plugin for its descendants.

Usage

Create a tour, add the steps, and start it. Steps join through steps.onboard or steps.register. There is no items option.

ts
import { createTour } from '@vuetify/v0'

const tour = createTour()

tour.steps.onboard([
  {
    id: 'search',
    placement: 'bottom',
    enter: ({ done, activate }) => {
      const el = document.querySelector('[data-tour="search"]')
      if (el) activate(el)
      done()
    },
  },
  { id: 'settings' },
])

tour.start()
await tour.next()
tour.complete()

Start, move, and finish

  • start() opens the first step. start({ stepId }) opens that step instead.

  • next(), prev(), and step(n) do nothing while the tour is stopped, the current step is not ready, or you are already at that end.

  • step counts from 1. tour.step(3) opens the third step.

  • complete() finishes the tour and sets isComplete.

  • stop() closes the tour and leaves isComplete false.

  • reset() stops the tour, resets the form, and clears the steps and activators.

Tell the card where to sit

placement is top, bottom, left, right, or center. createTour stores the value. Tour.Content uses it in place of its own placement prop.

  • Set noActivator on a step that has no target. The card centers immediately.

  • activate() sets a 100px scroll margin on the target. deactivate() puts the previous margin back.

  • Extra fields you pass to onboard or register stay on the ticket. Type them through createTour’s generic so they survive.

Skip a step you do not want

Filter the list before you onboard. createTour keeps every step you give it.

ts
tour.steps.onboard(mobile ? steps.filter(step => step.id !== 'sidebar') : steps)

Let the step finish opening

enter runs when the step opens. The tour will not move until that step is ready.

  • Call done(), or return a promise, when enter accepts the context. next(), prev(), and step() wait for that.

  • Leave enter off and the step is ready as soon as it opens.

  • A handler with no parameters is ready immediately too. A parameter that only has a default value still counts as no parameters. Accept the context argument when you need to call done() yourself.

  • Return a promise and the tour waits for it, then marks the step ready. If enter throws, or the promise rejects, the step is still marked ready and a warning is logged.

  • Call tour.ready(stepId, visit) when the work continues after enter returns. visit is the number on the enter context. The call does nothing unless the tour is still active, that step is still selected, and visit is still the current one.

  • Call activate(el) to register this step’s target. It sets anchor-name to --tour-{id} and scrolls the element into view. Pass { scroll: false } to keep the page still.

  • Find the element yourself, usually with document.querySelector. createTour does not search the DOM for you.

  • deactivate() restores the previous anchor-name. Leaving the step already calls it.

Check a form before Next

Register a form field under the step id when Next should validate it.

  • next() and step() call form.submit(id) and stay on the step when it returns false.

  • prev() does not validate, and it does not emit completed.

  • A ticket’s enter, leave, and completed functions run, and steps emits those same names.

  • completed runs after a successful next(), after step(), and from complete(). It does not run for prev() or stop().

  • start() does nothing outside the browser.

Context / DI

createTourPlugin() provides one tour for the app. useTour() reads it.

A page that should run a different tour calls createTourContext and provides the result. Descendants of that page read the local tour. The rest of the app still reads the plugin.

ts
import { createTourContext } from '@vuetify/v0'

export const [useOnboardingTour, provideOnboardingTour, onboarding] =
  createTourContext({ namespace: 'app:onboarding' })

// In the page that owns this tour
provideOnboardingTour()

// In a child of that page
const tour = useOnboardingTour()
await tour.next()

Read the plugin tour, or a parent that already provided v0:tour, with useTour:

ts
import { useTour } from '@vuetify/v0'

const tour = useTour()
await tour.next()

Architecture

createTour keeps the steps, the activators, and a form. Opening a step runs enter, then waits for done() or ready(stepId, visit). next() and step() submit the form field registered under that step id, when one exists.

Tour Architecture

Use controls to zoom and pan. Click outside or press Escape to close.

Tour Architecture

prev() skips the form and does not emit completed. The highlight, the card, and the keyboard live on Tour.

Reactivity

PropertyReactiveNotes
isActiveShallowRef, readonly. True from start() until stop() or complete()
isCompleteShallowRef, readonly. True after complete(). start() and reset() clear it
isReadyShallowRef, readonly. False until enter calls done() or ready(stepId, visit). A step with no enter is ready immediately
isFirstTrue when selectedIndex === 0
isLastTrue when the selected step is the last
canGoBackisReady && !isFirst
canGoNextisReady && !isLast
selectedIdCurrent step id
totalA number from steps.size. Read tour.total, not tour.total.value
Tip

Navigation waits for isReady canGoNext and canGoBack stay false until done() runs, so Prev and Next stay disabled while a step is still opening. next(), prev(), and step() check the same flag.

Examples

Plain sequencer

Six steps, sequenced only by createTour. The demo draws a ring from selectedId. Start, Prev, Next, and Stop are the controls. On the last step, Next becomes Complete, because next() does nothing there.

useOnboarding.ts owns the tour. welcome has no enter, so it is ready immediately and nothing is activated. search and avatar find [data-tour] inside this demo and call activate with { scroll: false }, so the docs page does not jump. settings withholds done() until you press the button, so Next and Prev stay disabled. Opening that step with Prev passes direction back and calls done() immediately. name registers a form field under the same id. Next submits it and stays on the step while the name is empty. Prev does not validate. finish activates nothing. Complete runs its completed handler. Stop does not.

basic.vue renders the targets, the current title and body, and the buttons from isActive, isReady, canGoBack, canGoNext, and isLast. Nothing floats. The ring is the only mark.

Use this when the targets are already on the page and you only need the sequencer. The card and highlight ship as Tour.

FileRole
useOnboarding.tsCreates the tour, onboards the six steps, and holds the name field
basic.vueTargets, the step copy, and Start, Prev, Next, and Stop

Press Start.

Theme
Mode
Palettes
Accessibility

Recipes

Run one tour at a time

The plugin holds one step list. Keep each tour’s steps in the app. app.use belongs in main.ts. Call useTour() while a component is setting up, and close load over that tour.

Starting a tour stops whatever is running, clears that list, and onboards the chosen steps. Activators stay registered, so a target in the layout is found when its step opens. reset() clears activators too. Use stop() and steps.clear() instead.

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

app.use(createTourPlugin())
ts
// component setup
import { useTour } from '@vuetify/v0'

const tour = useTour()

const catalog = {
  search: [{ id: 'search' }, { id: 'search-input' }],
  examples: [{ id: 'intro' }, { id: 'preview' }],
}

function load (id: keyof typeof catalog) {
  tour.stop()
  tour.steps.clear()
  tour.steps.onboard(catalog[id])
  tour.start()
}

Filter steps before onboard

createTour does not filter. Remove the steps you do not want, then onboard what is left.

ts
const visible = mobile
  ? steps.filter(step => step.id !== 'sidebar')
  : steps

tour.steps.onboard(visible)

Gate next on a form field

Register validation under the same id as the step. next() and step() call form.submit(id) and stay on the step when it returns false. prev() does not validate.

ts
import { createTour, createValidation } from '@vuetify/v0'
import { shallowRef } from 'vue'

const tour = createTour()
const name = shallowRef('')
const validation = createValidation({
  value: name,
  rules: [
    value => String(value ?? '').trim().length > 0 || 'Enter a name.',
  ],
})

tour.form.register({ id: 'profile', value: validation })
tour.steps.onboard([{ id: 'profile' }, { id: 'done' }])

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for the useTour composable.
Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/