Skip to content

useOnboardingBuilder ​

The useOnboardingBuilder composable provides a fluent, type-safe builder API for constructing multi-phase onboarding tours on top of driver.js. It lets you declare named phases with onEnter/onExit lifecycle hooks and attach steps to each phase; the builder transparently wires phase transitions into driver.js navigation so that moving between steps across phases runs the correct hooks.

The resulting builder is consumed by the FirstRunOrchestrator component via its onboardingBuilder prop, which manages sequencing with the Disclaimer and Changelogs flows, cookie persistence, and the driver lifecycle. Internally the orchestrator renders the Onboarding component with the builder.

Features ​

  • Phased Tours: Split a tour into named phases, each with its own onEnter/onExit async hooks.
  • Fluent API: Chain addPhases → switchPhase → addSteps to describe the whole tour declaratively.
  • Type-Safe Phase Names: Phase names are inferred from a generic you pass to addPhases, so switchPhase only accepts valid names.
  • Lazy Titles & Descriptions: Step popover.title and popover.description accept either a string or a function (tOrFunc<string>), evaluated at build time.
  • Automatic Hook Wiring: When the user navigates across a phase boundary — including the final step of the tour — the previous phase's onExit and the next phase's onEnter are invoked automatically. Any navigation hooks you provide on steps or in the driver config are merged with the builder's handlers, never overwritten.
  • driver.js Integration: Produces a configured Driver instance via buildDriver, including i18n labels and Lucide footer-button icons.

Return Shape ​

useOnboardingBuilder(config?) returns:

MethodDescription
addPhasesRegisters the available phases and returns an OnboardingStepBuilder<Phases>.

The returned builder (OnboardingStepBuilder<Phases>) exposes:

MemberTypeDescription
addSteps(steps: OnboardingStep[]) => OnboardingStepBuilder<Phases>Appends steps to the currently active phase. Throws if the array is empty.
switchPhase(phase: Phases) => OnboardingStepBuilder<Phases>Switches the active phase. Throws if the phase is unknown or already active.
currentPhaseOnboardingPhase<Phases> | undefinedThe phase that is currently active (read-only).
buildDriver(config?: Config) => DriverBuilds a driver.js Driver instance from the accumulated steps and merges any extra config.

Types ​

OnboardingPhase<Phases> ​

ts
type OnboardingPhase<Phases> = {
  name: Phases;
  onEnter?: () => Promise<void>;
  onExit?: () => Promise<void>;
};

OnboardingStep ​

A step is a driver.js DriveStep with the popover type extended so that title and description accept either a string or a function (tOrFunc<string>). All other DriveStep and Popover properties (including element, onNextClick, onPrevClick, etc.) are preserved:

ts
type StepPopoverOverride = Omit<Popover, 'title' | 'description'> & {
  title?: tOrFunc<string>;
  description?: tOrFunc<string>;
};

type OnboardingStep = Omit<DriveStep, 'popover'> & {
  popover?: StepPopoverOverride;
};

type tOrFunc<T> = T | (() => T);

Usage ​

Basic Phased Tour ​

The builder is app-owned — each application defines its own tour. Construct it once (for example in app.vue) and pass it to the FirstRunOrchestrator, which owns when to mount the tour:

vue
<script lang="ts" setup>
const builder = useOnboardingBuilder()
  .addPhases<'Phase1' | 'Phase2'>([
    {
      name: 'Phase1',
      onEnter: async () => {
        console.log('enter phase 1');
      },
      onExit: async () => {
        console.log('exit phase 1');
      },
    },
    {
      name: 'Phase2',
      onEnter: async () => {
        console.log('enter phase 2');
      },
      onExit: async () => {
        console.log('exit phase 2');
      },
    },
  ])
  .switchPhase('Phase1')
  .addSteps([
    {
      popover: {
        title: 'Step 1',
        description: 'This is step 1',
      },
    },
    {
      popover: {
        title: 'Step 2',
        description: 'This is step 2',
      },
    },
  ])
  .switchPhase('Phase2')
  .addSteps([
    {
      popover: {
        title: 'Step 3',
        description: 'This is step 3',
      },
    },
  ]);
</script>

<template>
  <FirstRunOrchestrator
    :onboarding-builder="builder"
    :disclaimer="{
      appName: 'Test App',
      confirmationText:
        'I have read and understood the instructions and confirm that I will use Test App exclusively in compliance with the stated guidelines.',
    }"
  />
</template>

The orchestrator sequences the flows by priority (Disclaimer → Changelogs → Onboarding). The tour auto-starts on mount once all higher-priority flows have completed. If the onboardingBuilder prop is omitted, the Onboarding flow is skipped entirely.

Lazy Title and Description ​

Step text can be supplied as a function so it is evaluated when the driver is built (for example, to read fresh reactive state):

ts
const builder = useOnboardingBuilder()
  .addPhases<'intro'>([{ name: 'intro' }])
  .switchPhase('intro')
  .addSteps([
    {
      popover: {
        title: () => `Welcome, ${userName.value}`,
        description: () => t('tour.intro.description'),
      },
    },
  ]);

Passing Additional driver.js Config ​

useOnboardingBuilder accepts an optional driver.js Config that is merged into every driver it builds. Use it for global hooks or overrides:

ts
const builder = useOnboardingBuilder({
  allowClose: false,
  onDeselected: () => {
    console.log('user clicked away');
  },
}).addPhases<'tour'>([{ name: 'tour' }])
  .switchPhase('tour')
  .addSteps([/* ... */]);

TIP

Hooks you provide at the driver-config level (such as onDeselected, onHighlightStarted) are merged with the builder's own hooks via extendDriverHook, so they run before the builder's phase-transition logic. Similarly, step-level onNextClick/onPrevClick hooks are preserved and executed before the builder's navigation handlers.

How Phase Transitions Work ​

The builder is a small state machine (Initial → PhaseSwitched → StepsAdded). The state determines how hooks are attached when you call addSteps and switchPhase:

  1. Initial switchPhase before any steps — the phase's onEnter is hooked into driver.js' onHighlightStarted callback and runs only on the very first step of the tour.
  2. switchPhase after steps exist — the onNextClick handler of the last step in the outgoing phase is wired to:
    • await currentPhase.onExit?.()
    • await newPhase.onEnter?.()
    • driver.moveNext()
  3. addSteps immediately after a switchPhase — the onPrevClick handler of the first new step is wired to navigate back into the previous phase:
    • await currentPhase.onExit?.()
    • await oldPhase.onEnter?.()
    • driver.movePrevious()
  4. Last step of the tour — the onNextClick handler of the very last step is wired to invoke the current phase's onExit before advancing, ensuring the final phase's teardown always runs.

TIP

The builder merges user-provided hooks with its own navigation handlers via extendDriverHook, so any onNextClick/onPrevClick/onHighlightStarted hooks you define on a step or in the driver config are preserved and run before the builder's phase-transition logic. You can safely combine custom step-level hooks with the fluent builder API.

Errors ​

The builder throws synchronously when used incorrectly:

ConditionMessage
addSteps called with an empty arraysteps cannot be empty
switchPhase called with an unknown namePhase "<name>" not found
switchPhase called with the already-active phasePhase "<name>" is already the current phase

Consuming the Builder ​

The builder is designed to be handed to the FirstRunOrchestrator component, which handles flow sequencing, cookie persistence, and the driver lifecycle:

vue
<template>
  <FirstRunOrchestrator :onboarding-builder="builder" />
</template>

The orchestrator records tour completion in a tour-completed cookie when the user closes or finishes the tour. Use the OnboardingRestartButton component (included by default in the NavigationBar) to let users re-trigger the tour on demand.

To disable the onboarding flow entirely, set disableOnboarding in your runtime config:

typescript
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      commonUi: {
        disableOnboarding: true,
      },
    },
  },
});

For the full component behavior (orchestrator sequencing, cookie persistence, exposed methods), see the Onboarding component page.

i18n ​

The driver instance built by this composable is preconfigured with translation keys under common-ui.tour.*. See the Internationalization section for the available keys (skip, next, prev, finish, progress, restart).

Developed with ❤️ by the DCC. Documentation released under the MIT License.