Writing Tours
A Trailguide tour is a plain TypeScript/JSON object. It lives in your codebase, ships with your deployments, and gets reviewed in pull requests.
Tour structure
import type { Trail } from '@trailguide/core'
const tour: Trail = {
id: 'onboarding', // unique identifier
title: 'Get started', // used in analytics
version: '1.0.0', // bump when steps change significantly
theme: { // optional: per-trail CSS custom property overrides
'--trailguide-accent': '#6366f1',
'--trailguide-radius': '12px',
},
steps: [/* ... */],
}id
A stable unique string. Used as the key for analytics events and for tourStorage to track completion. If you change id all users will see the tour again.
version
Not enforced by the SDK — use it as a convention to track which version of a tour a user has seen, e.g. by storing it alongside the completion flag in your own backend.
theme
An optional map of CSS custom property overrides applied to this trail at runtime. Useful when you want different trails to use different brand colors without touching global CSS.
theme: {
'--trailguide-accent': '#6366f1',
'--trailguide-accent-hover': '#4f46e5',
'--trailguide-radius': '12px',
},Any variable from the full variable reference can be overridden here. The Pro Editor includes a live theme preview that applies these values in real time.
Step structure
import type { Step } from '@trailguide/core'
const step: Step = {
id: 'step-dashboard', // unique within the tour
target: '[data-tour="dashboard"]', // CSS selector for the highlighted element
placement: 'bottom', // tooltip position
title: 'Your dashboard', // tooltip heading
content: 'All your work in one place.', // tooltip body text
optional: false, // if true, silently skip if target not found
}placement
Controls which side of the target the tooltip appears on. Trailguide uses @floating-ui/dom under the hood, so it will flip and shift automatically to stay in viewport.
| Value | Description |
|---|---|
'top' | Above the target |
'bottom' | Below the target |
'left' | Left of the target |
'right' | Right of the target |
optional
When optional: true, if the target element is not found or is not visible, the step is silently skipped instead of showing an error tooltip. Useful for steps that point to elements that only appear in certain states.
{
id: 'step-pro-badge',
target: '[data-tour="pro-badge"]',
placement: 'left',
title: 'Pro features',
content: 'Upgrade to unlock these features.',
optional: true, // skipped for free users who don't have the badge
}Organizing tours
For small apps, a single tours.ts file works fine. For larger apps, consider co-locating tours with the pages they document:
app/
dashboard/
page.tsx
page.tour.ts ← tour for this page
settings/
page.tsx
page.tour.tsOr a shared lib/tours/ directory:
lib/
tours/
onboarding.ts
settings.ts
analytics.ts
index.ts ← re-exports all toursTrail modes
Every trail has an optional mode field:
const tour: Trail = {
id: 'onboarding',
title: 'Get started',
version: '1.0.0',
mode: 'both', // 'tour' | 'test' | 'both'
steps: [/* ... */],
}| Mode | Behavior |
|---|---|
'tour' | Shows onboarding tooltips to users. This is the default if mode is omitted. |
'test' | Runs as a Playwright test only. No tooltip UI is shown. |
'both' | Shows tooltips to users and runs as a Playwright CI test from the same file. |
Steps can include action, value, and assert fields for test execution:
{
id: 'step-create',
target: '[data-trail-id="create-btn"]',
placement: 'bottom',
title: 'Create a project',
content: 'Click here to get started.',
action: 'click',
assert: { type: 'visible' },
}See the Playwright guide for full setup instructions.
Step types
Every step has a stepType field that controls what UI it renders. The default is element (a tooltip anchored to a page element). All other types are full-screen overlays.
stepType | Description |
|---|---|
'element' | Tooltip anchored to a target element. Default. |
'announcement' | Full-screen modal with headline, body, optional image, badge, and up to two buttons. |
'checklist' | Interactive task list. Users check off items before continuing. Supports required vs optional tasks and per-task navigation URLs. |
'celebration' | Confetti moment. Shows a congratulations card with an emoji, message, and CTA button. |
'feedback' | In-tour feedback form. Supports stars, thumbs up/down, or NPS (0-10). Optional free-text comment. POSTs the response to a webhook URL you configure. |
'redirect' | Navigates the user to another URL mid-tour with an optional delay and message overlay. Use it to chain onboarding flows across pages. |
'delay' | Silent pause. No UI is shown. The tour waits for the configured number of milliseconds before advancing. Useful before navigations or animations settle. |
// Announcement step
{
id: 'announce-feature',
stepType: 'announcement',
title: 'Meet the new dashboard',
content: 'We rebuilt it from scratch. Here is what changed.',
announcement: {
badge: 'NEW',
imageUrl: 'https://cdn.example.com/dashboard-preview.png',
primaryCta: 'Show me',
secondaryCta: 'Maybe later',
},
}
// Celebration step
{
id: 'onboarding-complete',
stepType: 'celebration',
title: 'You did it!',
content: 'Your first project is live.',
celebration: {
emoji: '🎉',
showConfetti: true,
ctaLabel: 'Go to dashboard',
},
}
// Feedback step
{
id: 'rate-experience',
stepType: 'feedback',
title: 'How did we do?',
feedback: {
type: 'stars',
question: 'How would you rate your onboarding experience?',
allowComment: true,
commentPlaceholder: 'Any other thoughts?',
webhook: 'https://your-api.com/feedback',
ctaLabel: 'Submit',
},
}
// Checklist step
{
id: 'getting-started',
stepType: 'checklist',
title: 'Get set up',
checklist: {
items: [
{ id: 'item-1', label: 'Connect your data source', url: '/settings/data', required: true },
{ id: 'item-2', label: 'Invite your team', url: '/settings/team', required: false },
],
ctaLabel: 'Done',
allowSkip: false,
},
}In the Pro Editor, all step types appear in the drag-and-drop toolbar. Drop any type into any position in the flow. The step editor adapts to show the relevant configuration fields for the selected type.
Versioning strategy
Tours are just data. Treat them like any other content in your codebase:
- Commit tour changes in the same PR as the feature they document
- Review tour copy in code review alongside the UI changes
- Use the
versionfield to communicate breaking changes - Export tour JSON for use with Trailguide Pro’s visual editor.
Tip: Keep step id values stable. They are used as the primary key in analytics reports — changing an id breaks historical step-level metrics.