useTour
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.
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.
Start, move, and finish
start()opens the first step.start({ stepId })opens that step instead.next(),prev(), andstep(n)do nothing while the tour is stopped, the current step is not ready, or you are already at that end.stepcounts from 1.tour.step(3)opens the third step.complete()finishes the tour and setsisComplete.stop()closes the tour and leavesisCompletefalse.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
noActivatoron 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
onboardorregisterstay on the ticket. Type them throughcreateTour’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.
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, whenenteraccepts the context.next(),prev(), andstep()wait for that.Leave
enteroff 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
enterthrows, or the promise rejects, the step is still marked ready and a warning is logged.Call
tour.ready(stepId, visit)when the work continues afterenterreturns.visitis the number on the enter context. The call does nothing unless the tour is still active, that step is still selected, andvisitis still the current one.Call
activate(el)to register this step’s target. It setsanchor-nameto--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 previousanchor-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()andstep()callform.submit(id)and stay on the step when it returns false.prev()does not validate, and it does not emitcompleted.A ticket’s
enter,leave, andcompletedfunctions run, andstepsemits those same names.completedruns after a successfulnext(), afterstep(), and fromcomplete(). It does not run forprev()orstop().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.
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:
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.
prev() skips the form and does not emit completed. The highlight, the card, and the keyboard live on Tour.
Reactivity
| Property | Reactive | Notes |
|---|---|---|
isActive | ShallowRef, readonly. True from start() until stop() or complete() | |
isComplete | ShallowRef, readonly. True after complete(). start() and reset() clear it | |
isReady | ShallowRef, readonly. False until enter calls done() or ready(stepId, visit). A step with no enter is ready immediately | |
isFirst | True when selectedIndex === 0 | |
isLast | True when the selected step is the last | |
canGoBack | isReady && !isFirst | |
canGoNext | isReady && !isLast | |
selectedId | Current step id | |
total | A number from steps.size. Read tour.total, not tour.total.value |
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
Press Start.
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.
// main.ts
import { createTourPlugin } from '@vuetify/v0'
app.use(createTourPlugin())// 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.
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.
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
next() stays put for one of these reasons:
The tour is not active.
The current step is not ready. If
enteraccepts a context, calldone()or return a promise. Until thenisReadyis false, andprev()andstep()wait too.You are already on the last step.
A form field is registered under this step id, and
form.submit(id)returned false.
stop()closes the tour.isCompletestays false, andcompleteddoes not run.complete()runs the current step’scompletedhandler and event, closes the tour, and setsisCompleteto true.reset()stops the tour, resets the form, and clears the steps and activators.
step counts from 1. tour.step(3) opens the third step (steps.lookup(2)). Leaving a form step still validates. The step you leave emits completed. The step you open receives direction jump.
total is a number, read from steps.size. Use tour.total. isActive, isReady, and selectedId are refs.
createTourPlugin() provides one tour for the app. It does not store the list of tours. The app loads one tour at a time, as in the recipe above. A page can still provide its own tour with createTourContext. The highlight, the card, and the keyboard are on Tour.