# Configuration

> Explore every Driver.js configuration option. Configure the driver globally, per step, or on the fly, with full TypeScript autocomplete and type safety.

Pass options to `driver()`. Options marked _step_ can also be set on a single step, where they override the driver's value.

```js
import { driver } from "driver.js";
import "driver.js/dist/driver.css";

const driverObj = driver({
  showProgress: true,
  overlayOpacity: 0.5,
  steps: [
    {
      element: "#search",
      popover: { title: "Search", description: "Find anything." },
    },
    {
      element: "#export",
      disableActiveInteraction: true,
      popover: { title: "Export", side: "left" },
    },
  ],
});

driverObj.drive();
```

Each `driver()` call is its own instance with its own configuration, steps and state. Only one instance is on screen at a time: starting one closes any other. To change options later, use [`setConfig()`](https://driverjs.com/docs/api.md#configuration).

All types on this page are exported from `driver.js`.

## Driver Configuration

```typescript
type Config = {
  // The steps of the tour. Not needed for highlight().
  steps?: DriveStep[];

  // --- Overlay and highlight ---

  // Animate between steps. (default: true)
  animate?: boolean;
  // Length of that animation and of the fade-in, in milliseconds.
  // (default: 400)
  duration?: number;
  // Any CSS color. (default: "#000")
  overlayColor?: string;
  // (default: 0.7)
  overlayOpacity?: number;
  // Space between the element and the edge of the cutout, in pixels.
  // (default: 10)
  stagePadding?: number;
  // Corner radius of the cutout, in pixels. (default: 5)
  stageRadius?: number;
  // Scroll smoothly to the highlighted element. (default: false)
  smoothScroll?: boolean;
  // Let the page scroll while the tour is open. (default: true)
  allowScroll?: boolean;

  // --- Closing and navigation ---

  // Let the user close the tour: with the close button, Escape or a click
  // on the overlay. false also hides the close button. (default: true)
  allowClose?: boolean;
  // What a click on the overlay does. A function replaces the default and
  // receives the same arguments as a hook. (default: "close")
  overlayClickBehavior?: "close" | "nextStep" | "none" | DriverHook;
  // Arrow keys move between steps and Escape closes. (default: true)
  allowKeyboardControl?: boolean;

  // --- Interaction (all step) ---

  // Block the highlighted element: true turns off all pointer events
  // on it, "clicks" keeps hover and blocks clicks. (default: false)
  disableActiveInteraction?: boolean | "clicks";
  // Complete the step when this event happens on the highlighted element.
  // (default: off)
  advanceOn?: false | string | string[] | AdvanceOnEvent;
  // Milliseconds to pause after advanceOn or advance() completes a step,
  // before moving on. (default: 0)
  advanceDelay?: number;
  // Other elements the user may use during a step, like a menu it opens.
  allowedElements?: AllowedElements;
  // Called for every click during a step. Return false to block it.
  onInteraction?: InteractionHook;
  // Deprecated: use advanceOn: "click".
  advanceOnClick?: boolean;

  // --- Missing elements (all step) ---

  // Milliseconds to wait for a step's element to appear.
  // (default: 0, no wait)
  waitForElement?: number;
  // Skip a step whose element is not on the page. (default: false)
  skipMissingElement?: boolean;
  // What to do when the highlighted element leaves the page for good, or a
  // wait runs out. (default: "stay")
  onElementMissing?: ElementMissing;

  // --- Popover defaults (see Popover Configuration) ---

  popoverClass?: string;
  // Space between the popover and the cutout, in pixels. (default: 10)
  popoverOffset?: number;
  // (default: ["next", "previous", "close"] in a tour, [] in highlight())
  showButtons?: ("next" | "previous" | "close")[];
  disableButtons?: ("next" | "previous" | "close")[];
  showProgress?: boolean;
  progressText?: string;
  nextBtnText?: string;
  prevBtnText?: string;
  doneBtnText?: string;
  closeBtnLabel?: string;

  // --- Hooks (see Hooks) ---

  onPopoverRender?: (popover: PopoverDOM, opts: HookOpts) => void;
  onHighlightStarted?: DriverHook;
  onHighlighted?: DriverHook;
  onDeselected?: DriverHook;
  onDestroyStarted?: DriverHook;
  onDestroyed?: DriverHook;
  onNextClick?: DriverHook;
  onPrevClick?: DriverHook;
  onCloseClick?: DriverHook;
  onDoneClick?: DriverHook;
};
```

## Step Configuration

A step is what you put in `steps` or pass to `highlight()`.

```typescript
type DriveStep = {
  // What to highlight: a CSS selector (first match), an element, or a
  // function returning one. Without it the popover is centered on the
  // screen.
  element?: string | Element | (() => Element);

  // The popover of this step. See Popover Configuration.
  popover?: Popover;

  // Your own data, readable from the step in any hook.
  data?: Record<string, any>;

  // Per-step versions of the driver options with the same name.
  disableActiveInteraction?: boolean | "clicks";
  advanceOn?: false | string | string[] | AdvanceOnEvent;
  advanceDelay?: number;
  allowedElements?: AllowedElements;
  onInteraction?: InteractionHook;
  waitForElement?: number;
  skipMissingElement?: boolean;
  onElementMissing?: ElementMissing;
  advanceOnClick?: boolean;

  // Per-step hooks. They run instead of the driver's.
  onHighlightStarted?: DriverHook;
  onHighlighted?: DriverHook;
  onDeselected?: DriverHook;
};
```

If a step names an element that is not on the page, its popover is centered like a step without one, unless [`waitForElement` or `skipMissingElement`](#missing-elements) says otherwise.

## Popover Configuration

Set on a step's `popover`. The options that also exist on the driver fall back to the driver's value.

```typescript
type Popover = {
  // HTML is allowed in both. Leave one out to show only the other.
  title?: string;
  description?: string;

  // Side of the element the popover sits on. It flips when it doesn't fit.
  // (default: "bottom")
  side?: "top" | "right" | "bottom" | "left";
  // Position along that side. (default: "start")
  align?: "start" | "center" | "end";

  // Buttons to show, and buttons to show but disable.
  showButtons?: ("next" | "previous" | "close")[];
  disableButtons?: ("next" | "previous" | "close")[];

  // (defaults: "Next", "Previous", "Done")
  nextBtnText?: string;
  prevBtnText?: string;
  // Text of the next button on the last step.
  doneBtnText?: string;
  // Screen reader label of the close button. (default: "Close")
  closeBtnLabel?: string;

  // Show the step count. (default: false)
  showProgress?: boolean;
  // {{current}} and {{total}} are replaced.
  // (default: "{{current}} of {{total}}")
  progressText?: string;

  // Extra class on the popover, for styling.
  popoverClass?: string;

  // Makes the step an action step: the user has to do this to move on.
  // HTML is allowed. See Action Steps below.
  instruction?: string;

  // Per-step hooks. They run instead of the driver's.
  onPopoverRender?: (popover: PopoverDOM, opts: HookOpts) => void;
  onNextClick?: DriverHook;
  onPrevClick?: DriverHook;
  onCloseClick?: DriverHook;
  onDoneClick?: DriverHook;
};
```

Examples for position, buttons and custom buttons are in the [Popover guide](https://driverjs.com/docs/popover.md).

### Action Steps

An action step is a step with `popover.instruction`. The instruction shows in its own row, ending in a pulsing dot while the step waits, and the step only completes through `advanceOn`, [`advance()`](https://driverjs.com/docs/api.md#navigation) or `moveNext()`. The Next button, when `showButtons` has it, stays in its place but looks disabled (`aria-disabled`): pressing it, the right arrow key or the overlay shakes the popover instead of moving on, and `onNextClick` doesn't run. Previous, the close button and Escape work as usual. With `advanceDelay`, the dot becomes a check mark and the row and Next turn green for the pause; screen readers hear your `doneBtnText` ("Done") once. In `highlight()` the instruction is a plain line and nothing is gated.

```js
{
  element: "#inbox",
  advanceOn: "click",
  popover: {
    title: "Open your inbox",
    instruction: "Click <b>Inbox</b>.",
  },
}
```

In `highlight()`, `action` is only an instruction line. See [Action-Driven Tours](https://driverjs.com/docs/action-driven-tours.md) for live examples.

## Interaction

By default the user can use the highlighted element, and nothing else on the page. These options change that.

| You want to... | Use |
| --- | --- |
| Move on when the user clicks, types or picks something | `advanceOn: "click"`, `"input"`, `"change"`, ... |
| Move on only when the result is right | `advanceOn: { event, when }` |
| Accept one gesture on the element and block other clicks | `advanceOn: { event, strict: true }` |
| Take away the Next button so the user has to act | [`popover.instruction`](#action-steps) |
| Let the user use something outside the element, like a menu | `allowedElements` |
| Let the user look but not touch | `disableActiveInteraction: true` |
| Keep hover and tooltips, block clicks | `disableActiveInteraction: "clicks"` |
| Decide per click, or react to a wrong click | `onInteraction` |
| Choose what a click on the dimmed page does | `overlayClickBehavior` |

### advanceOn

```typescript
type AdvanceOnEvent = {
  event: string | string[];
  when?: (
    element: Element,
    step: DriveStep,
    opts: HookOpts & { event: Event }
  ) => boolean;
  strict?: boolean;
};
```

- `event` is any DOM event name that reaches the highlighted element, or a list of them.
- `when` runs after your app has handled the event. The step completes only when it returns `true`.
- `strict: true` makes the element take only that gesture: other clicks, right-clicks and double-clicks on it are blocked and shake the popover. It applies when every event is a pointer gesture (`click`, `dblclick`, `contextmenu`, `auxclick`, pointer and mouse down and up).
- `false` on a step turns off the driver's `advanceOn` for it.

Completing a step runs `onNextClick` like the Next button does, or `onDoneClick` on the last step.

### onInteraction

```typescript
type InteractionHook = (
  element: Element | undefined,
  step: DriveStep,
  opts: HookOpts & {
    event: MouseEvent;
    region: "element" | "allowed" | "outside";
  }
) => false | void;
```

Called for every `click`, `auxclick`, `contextmenu` and `dblclick` during a step, before your app's handlers. `region` says where it happened: on the highlighted element, on one of the `allowedElements`, or anywhere else. Return `false` to block the event, including the `overlayClickBehavior` it would trigger. Clicks on the popover and key presses are not reported.

### allowedElements

```typescript
type AllowedElements =
  | string
  | Element
  | Array<string | Element>
  | (() => Element[] | NodeListOf<Element> | null | undefined);
```

Selectors, elements, or a function returning elements. They stay clickable, sit above the overlay and take part in Tab navigation. Selectors and functions are evaluated again as the page changes, so elements that appear later are included. If one still sits under the overlay, set `--driver-allowed-z-index` or target its positioned wrapper. When the highlighted element is inside an allowed element, like an item in an open menu, it gets the `driver-active-element-raised` class and a ring instead of the cutout; see [Theming](https://driverjs.com/docs/theming.md#css-variables).

### overlayClickBehavior

`"close"` ends the tour, `"nextStep"` moves on, `"none"` does nothing. On an [action step](#action-steps), `"close"` and `"nextStep"` shake the popover instead. `"nextStep"` does nothing while a step waits for its element. A function runs in every case.

### How Clicks Are Handled

While a tour runs, only the highlighted element, `allowedElements` and the popover take pointer events. A press anywhere else belongs to the tour, and your app never sees it:

- Its `pointerdown`, `mousedown`, `pointerup`, `mouseup`, `click`, `touchstart` and `touchend` are stopped on `window` in the capture phase. Only `window` capture listeners added before the tour started still run.
- Mouse events are also canceled, so focus stays where it was. Touch events are not, so scrolling works.
- A drag that starts in the highlighted element ends normally, wherever it is released.

The same goes for presses on an element with `disableActiveInteraction`, which also blocks the click that Enter or Space makes on it.

Only what the user does is stopped. Events your code dispatches, like `element.click()`, go through.

## Missing Elements

| Situation | Option | What happens |
| --- | --- | --- |
| The next element renders late | `waitForElement: 3000` | The current step stays, with its popover in a waiting state, until the element appears or the time runs out |
| The element may not exist at all | `skipMissingElement: true` | The step is skipped in the direction of travel |
| The highlighted element is replaced | nothing | The step follows the new element matching its selector |
| The highlighted element is removed | `onElementMissing` | See below |

`onElementMissing` takes a preset or a function:

- `"stay"` (default): the step stays where the element was.
- `"previous"`: go back one step.
- `"previousAction"`: go back to the last [action step](#action-steps) whose element is still on the page, or one step back if there is none.

With `waitForElement` set, a removed element first gets that long to come back. `"previous"` and `"previousAction"` also keep the current step when the next step's `waitForElement` runs out, instead of showing that step without its element.

```typescript
type ElementMissing =
  | "stay"
  | "previous"
  | "previousAction"
  | ElementMissingHook;

type ElementMissingHook = (
  element: Element | undefined,
  step: DriveStep,
  opts: HookOpts & { cause: "removed" | "timeout" }
) => false | void;
```

A function is called with `cause: "removed"` when the highlighted element is gone, and decides what to do. It is called with `cause: "timeout"` when a step's `waitForElement` runs out, with that step and its index; the step then shows without its element unless the function returns `false` or navigates.

Examples: [Wait for Elements](https://driverjs.com/docs/interactive-tours.md#wait-for-elements), [Optional Steps](https://driverjs.com/docs/interactive-tours.md#optional-steps), [menus](https://driverjs.com/docs/action-driven-tours.md#let-the-user-use-a-menu) and [slow loads](https://driverjs.com/docs/action-driven-tours.md#recover-from-a-slow-load).

## Hooks

```typescript
type DriverHook = (
  element: Element | undefined,
  step: DriveStep,
  opts: HookOpts
) => void;

type HookOpts = {
  config: Config;
  state: State;
  driver: Driver;
  // Index of the active step. undefined in highlight().
  index: number | undefined;
  // Why the tour is ending. Only in the destroy hooks.
  reason?: "done" | "close" | "escape" | "overlay" | "replaced" | "api";
  // Aborts when the step is left. Only in onHighlightStarted and
  // onHighlighted.
  signal?: AbortSignal;
};
```

| Hook | Runs |
| --- | --- |
| `onHighlightStarted` | Before a step's element is highlighted |
| `onHighlighted` | Once the highlight has settled on the element |
| `onDeselected` | When a step is left, and when the tour ends |
| `onDestroyStarted` | When the user tries to end the tour. The tour only ends if you call `destroy()` |
| `onDestroyed` | After the tour has ended |
| `onPopoverRender` | After the popover is added to the page, before it is positioned. Change its DOM here |
| `onNextClick`, `onPrevClick`, `onCloseClick` | Instead of the button's default action |
| `onDoneClick` | Instead of `onNextClick` on the last step |

- On the last step the order is: the step's `onDoneClick`, the driver's `onDoneClick`, the step's `onNextClick`, the driver's `onNextClick`. A driver-level `onDoneClick` therefore runs instead of a step's own `onNextClick` there.
- On an [action step](#action-steps), pressing the disabled Next, the right arrow key or the overlay doesn't run `onNextClick`; completing the step does.
- The click hooks replace the default. Call `moveNext()`, `movePrevious()` or `destroy()` yourself. `onNextClick` also runs for the right arrow key, `overlayClickBehavior: "nextStep"`, `advanceOn` and `advance()`.
- `index` is always the active step. Inside the `onDeselected` that runs between steps, that is the step being moved to.
- `signal` cleans up listeners for you: `window.addEventListener("saved", onSaved, { signal })`.
- `destroy()` does not run `onDestroyStarted`. Neither does a tour that is replaced by another instance.

### Why a Tour Ended

`onDestroyStarted`, `onDestroyed` and the `onDeselected` that runs on destroy get `opts.reason`:

| Reason | Meaning |
| --- | --- |
| `"done"` | Moved past the last step, including when the remaining steps were skipped |
| `"close"` | The close button |
| `"escape"` | The Escape key |
| `"overlay"` | A click on the overlay |
| `"replaced"` | Another driver instance started |
| `"api"` | Your code called `destroy()`, or `moveTo()` / `movePrevious()` outside the steps |

A `destroy()` you call from a hook keeps the reason of the user action that started it. See [Confirm Before Closing](https://driverjs.com/docs/confirm-before-closing.md) and [Track Tour Analytics](https://driverjs.com/docs/track-tour-analytics.md) for examples.

## State

`driverObj.getState()` returns the state, and hooks get it as `opts.state`.

```typescript
type State = {
  // Whether a tour or highlight is showing.
  isInitialized?: boolean;

  // Index, element and step that are active. No index in highlight().
  activeIndex?: number;
  activeElement?: Element;
  activeStep?: DriveStep;

  // Element and step that were active before.
  previousElement?: Element;
  previousStep?: DriveStep;

  // The popover's DOM elements.
  popover?: PopoverDOM;
};
```

`PopoverDOM` is described in [Theming](https://driverjs.com/docs/theming.md#changing-the-popover-dom).

## Hints Configuration

Passed to `hints()` from `driver.js/hints`. See the [Hints guide](https://driverjs.com/docs/hints.md) for examples.

```typescript
type HintsConfig = {
  hints?: DriverHint[];

  // Beacon defaults for every hint. A hint's own values win.
  beacon?: HintBeacon;

  // Text of the dismiss button. (default: "Got it")
  buttonText?: string;

  // Same meaning as in tours.
  popoverClass?: string;
  popoverOffset?: number;

  // Dim the page while a hint is open, with its element cut out.
  // (default: false)
  overlay?: boolean;
  // (defaults: "#000" and 0.7)
  overlayColor?: string;
  overlayOpacity?: number;

  // Called when a hint opens, and when it is dismissed.
  onOpen?: HintHook;
  onDismiss?: HintHook;
  // Runs instead of dismissing when the button is clicked.
  onButtonClick?: HintHook;
};

type HintHook = (
  element: Element,
  hint: DriverHint,
  opts: { config: HintsConfig; hints: Hints }
) => void;
```

```typescript
type DriverHint = {
  // A hint whose element is missing is skipped, and picked up on a
  // later show().
  element: string | Element | (() => Element);

  // Used by open(), dismiss() and restore(), and to save dismissals.
  // (default: the hint's index)
  id?: string;

  beacon?: HintBeacon;

  popover?: {
    title?: string;
    description?: string;
    side?: "top" | "right" | "bottom" | "left";
    align?: "start" | "center" | "end";
    popoverClass?: string;

    // The dismiss button. (default: true)
    showButton?: boolean;
    buttonText?: string;
    onButtonClick?: HintHook;

    onPopoverRender?: (
      popover: PopoverDOM,
      opts: { hint: DriverHint; hints: Hints }
    ) => void;
  };

  // Run instead of the hooks in HintsConfig.
  onOpen?: HintHook;
  onDismiss?: HintHook;

  data?: Record<string, any>;
};

type HintBeacon = {
  // Edge of the element the beacon sits on, and where along it.
  // (defaults: "top" and "end")
  side?: "top" | "right" | "bottom" | "left";
  align?: "start" | "center" | "end";
  // Move the beacon by this many pixels. Positive is right and down.
  offsetX?: number;
  offsetY?: number;
  // Pulse. Off for users who prefer reduced motion. (default: true)
  animate?: boolean;
  className?: string;
};
```

## Checklist Configuration

Passed to `checklist()` from `driver.js/checklist`. See the [Checklists guide](https://driverjs.com/docs/checklists.md) for examples.

```typescript
type ChecklistConfig = {
  // Identifies the checklist in events.
  id: string;
  items: ChecklistItem[];

  // (defaults: "Get started", none, the title)
  title?: string;
  description?: string;
  launcherText?: string;

  // Shown instead once every item is done.
  completeTitle?: string;
  completeDescription?: string;
  completeLauncherText?: string;

  // {{done}}, {{total}}, {{remaining}} and {{percent}} are replaced.
  // (default: "{{done}} of {{total}}")
  progressText?: string;
  // The count on the launcher. "" hides it.
  // (default: "{{done}}/{{total}}")
  launcherProgressText?: string;

  // false hides the launcher. Open the panel yourself with open() or
  // toggle().
  // (default: true)
  launcher?: boolean;
  // (default: "bottom-right")
  position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
  // Distance from the screen edges, in pixels. (default: 20, 12 on phones)
  offset?: { x?: number; y?: number };
  className?: string;

  // Let users tick and untick items with the check circle.
  // (default: false)
  allowManualComplete?: boolean;

  // Ids of the items that are already done.
  completed?: string[];
  // Called on every change.
  onProgress?: (progress: {
    completed: string[];
    done: number;
    total: number;
  }) => void;

  // Called when an item is ticked, and once when all are.
  onItemComplete?: (
    item: ChecklistItem,
    opts: { checklist: Checklist }
  ) => void;
  onComplete?: (opts: { checklist: Checklist }) => void;

  // Called with every event. See the Checklists guide for the types.
  onEvent?: (event: ChecklistEvent) => void;
};
```

```typescript
type ChecklistItem = {
  // Saved progress is a list of these, so keep them stable.
  id: string;
  // HTML is allowed in both.
  title: string;
  description?: string;

  // What a click does. A tour ticks the item when the user finishes it,
  // a link or an action ticks it on click.
  tour?: DriveStep[] | Config | Driver;
  href?: string;
  action?: (item: ChecklistItem, opts: { checklist: Checklist }) => void;

  // Ticks the item once it returns true, instead of the rules above.
  completeWhen?: () => boolean;

  // Overrides the checklist's allowManualComplete.
  allowManualComplete?: boolean;

  data?: Record<string, any>;
};
```
