Skip to main content

Vue guide

Install the app-scoped runtime and use typed definitions in plain Vue.

Use Nuxt Tour in plain Vue

Create a definition in src/tours/onboarding.ts:

ts
import { defineTour } from '@lupinum/nuxt-tour/vue'

export default defineTour({
  id: 'onboarding',
  steps: [{
    id: 'create-project',
    target: 'create-project',
    title: 'Create a project',
    content: 'Start your first project here.',
    interaction: 'target',
  }],
})

Install that definition and the theme in src/main.ts:

ts
import { createApp } from 'vue'
import { createTourPlugin } from '@lupinum/nuxt-tour/vue'
import '@lupinum/nuxt-tour/style.css'
import App from './App.vue'
import onboarding from './tours/onboarding'

createApp(App)
  .use(createTourPlugin({ tours: [onboarding] }))
  .mount('#app')

Render one host in src/App.vue. Pass the installed definition to useTour() so its step IDs remain literal:

vue
<script setup lang="ts">
import { ref } from 'vue'
import { TourHost, useTour } from '@lupinum/nuxt-tour/vue'
import onboarding from './tours/onboarding'

const tour = useTour(onboarding)
const error = ref('')

async function start() {
  error.value = ''
  try {
    await tour.start()
  }
  catch (cause) {
    error.value = cause instanceof Error ? cause.message : 'The tour could not start.'
  }
}
</script>

<template>
  <button v-tour-target="'create-project'">Create project</button>
  <button @click="start">Start tour</button>
  <p v-if="error" role="alert">{{ error }}</p>
  <TourHost />
</template>

The same plugin object can be installed on multiple Vue applications. Each app gets an independent runtime, targets, listeners, and active session.

Vue Router

Pass a Vue Router instance when a definition contains route:

ts
const app = createApp(App)
app.use(router)
app.use(createTourPlugin({ tours: [onboarding], router }))
app.mount('#app')

A route without a router rejects with ROUTER_NOT_CONFIGURED; it is never silently ignored. When the router provides afterEach, navigation outside the tour cancels the active or starting session.

Dynamic targets

vue
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import { useTourTarget } from '@lupinum/nuxt-tour/vue'

const saveButton = useTemplateRef<HTMLButtonElement>('saveButton')
useTourTarget('save-project', saveButton)
</script>

<template>
  <button ref="saveButton">
    Save project
  </button>
</template>

Use v-tour-target="'save-project'" for template targets and useTourTarget() for dynamic refs. Both register the semantic ID with the current Vue application, so multiple apps on one page stay isolated. Duplicate visible registrations fail clearly. HTML and SVG elements are supported. Use an explicit { selector } target only for third-party or legacy markup outside Vue.