Type to search Tours, Hints, options, and events.

to navigate · Enter to open · Esc to close

Documentation

Control and persistence

Start, drive, and remember Tours and Hints from Stimulus actions.

Tours and Hints each have their own Stimulus controller. Templates print the trigger with ux_tour_action() and ux_hints_action(); the same attributes can be written by hand. Nothing here requires application JavaScript.

Autostart

Tours default to autostart=false; Hints default to autostart=true. The asymmetry is deliberate: a Tour takes the screen away from someone, a beacon does not.

Component

<twig:Driver:Tour id="welcome" :autostart="true">
    ...
</twig:Driver:Tour>

<twig:Driver:Hints id="help" :autostart="false">
    ...
</twig:Driver:Hints>

Twig builder

{% set tour = create_tour('welcome') %}
{% set hints = create_hints('help') %}

{# A builder Tour always waits: give it a trigger. #}
<button {{ ux_tour(tour) }} {{ ux_tour_action('start') }}>Start tour</button>

{# Hints autostart unless the second argument says otherwise. #}
<div {{ ux_hints(hints, false) }}></div>

PHP builder

$tour = $this->tourBuilder->create('welcome');
$hints = $this->hintsBuilder->create('help');

autostart is the one setting with no builder method — it is decided when the template prints the attributes, with ux_tour_action('start') for a Tour and ux_hints(hints, false) for a Hints group.

Builders keep the same defaults: ux_tour(tour) renders a Tour that waits, and ux_hints(hints) autostarts unless you pass false as the second argument.

once

once is a UX Driver option, not a Driver.js one. With :once="true" on the component, .once() on the Twig builder, or ->once() on the PHP builder, the controller writes ux-driver:seen:<id> to window.localStorage and refuses to start that Tour again — autostart and manual triggers both return early.

Component

<twig:Driver:Tour id="welcome" :once="true">
    ...
</twig:Driver:Tour>

Twig builder

{% set tour = create_tour('welcome').once() %}

PHP builder

$tour = $this->tourBuilder->create('welcome')->once();

The flag is written at exactly one moment: when someone clicks the done button on the last Step. That narrow rule has consequences worth knowing:

  • Closing the Tour, pressing Escape, or navigating away leaves it unseen, so it comes back next time.
  • Calling preventDefault() on the cancelable ux-driver:done event also prevents the write.
  • If localStorage is unavailable — private browsing, blocked cookies, a full quota — the controller catches the failure, treats the Tour as unseen, and shows it again. The page keeps working; the Tour simply stops being once-only.

Hints have no equivalent. Driver.js tracks dismissed beacons on the live instance only, and a reload brings them all back.

Restarting

start and highlight both destroy the current Driver.js instance before creating the next one, so a second trigger always yields a clean Tour from Step one — unless moveTo sends it elsewhere.

Tour actions

<button {{ ux_tour_action('start') }}>Start tour</button>
<button {{ ux_tour_action('next') }}>Next</button>
<button {{ ux_tour_action('previous') }}>Previous</button>
<button {{ ux_tour_action('refresh') }}>Refresh position</button>
<button {{ ux_tour_action('destroy') }}>Close tour</button>

<button {{ ux_tour_action('moveTo', {index: 2}) }}>Go to step 3</button>

moveTo requires the index param and counts from zero. Omitting it does nothing.

Hints actions

<button {{ ux_hints_action('show') }}>Show hints</button>
<button {{ ux_hints_action('hide') }}>Hide hints</button>
<button {{ ux_hints_action('close') }}>Close the open popover</button>
<button {{ ux_hints_action('restoreAll') }}>Restore dismissed hints</button>

Four more actions take a hintId param. All eight, with their parameters, are in the Hints reference.

Next