Skip to content

Reference

Tour schema

Every field of the tour JSON document.

A tour is one JSON document. This page lists every field. All fields are optional unless marked required, and every type is exported from @docentjs/core and re-exported by the other packages.

The schema is published at docentjs.dev/schema/tour-v1.json. Tours written as .json files can point at it, which gives most editors completion and checking as you type:

{
"$schema": "https://docentjs.dev/schema/tour-v1.json",
"id": "welcome",
"steps": [{ "id": "intro", "title": "Welcome" }]
}

The guides explain each area with examples: steps, targets, triggers and conditions, beacons, theming, and arrows, spotlight and overlay.

Field Type Notes
schemaVersion 1 required; defineTour sets it
id string required; used for persistence, targeting, analytics
version number bump to re-show the tour to users who saw an older version
name, description string for the builder and dashboards
steps Step[] required
trigger Trigger what starts the tour
conditions Condition[] all must hold for the tour to be eligible
options TourOptions see below
meta Record<string, unknown> free-form extension bag
Field Type Default
persist boolean –
frequency 'once' | 'until-completed' | 'always' 'once'
showProgress boolean true
progress ProgressStyle 'meter'; see Themes
eyebrow string – ; a small line above every title. {tour} becomes the tour’s name
allowClose boolean true (Escape and the close button)
closeOnOverlayClick boolean false
closeOnOutsideClick boolean true for beacon tours, else false; a click anywhere outside the popover closes it
keyboard boolean true (arrow keys)
arrow ArrowStyle 'caret'; see Arrows, spotlight and overlay
spotlight { padding?, radius?, shape?, ring?, animate? } 8, 10, 'rounded', 'hairline', true
overlay { style?, color?, opacity?, blur? } 'dim', tinted ink, 0.52, 4
beacon { style?, text?, position?, offset?, size? } 'pulse', 'New', 'top-right', 0, 10; see Beacons
mobile { layout?, card? } layout: 'auto' (beside the target when the card fits, docked when it does not), 'float' never docks, 'dock' always does. card: 'stories' (segmented progress on top, full-width main button), 'compact' (a small card beside the element, pointing at it) or 'classic'. See Small screens
scroll { enabled?, behavior?, block? } true, 'auto', 'center'
labels Labels English defaults; progress supports {current}, {total}, {current2}, {total2}
theme ThemeSpec a preset name, tokens, or { preset, ...tokens }
appearance 'light' | 'dark' | 'auto' 'light'; auto follows the system setting
template string a built-in look (spotlight, hint, announcement), a theme, or a template the app registered
minViewportWidth number – ; in px. Below this viewport width the tour never shows, e.g. 768 to skip phones
Field Type Notes
id string required; unique within the tour
target Target omit for a centred modal step
title, body string
eyebrow string overrides the tour’s eyebrow; '' removes it
format 'text' | 'markdown' markdown subset: bold, italic, code, links, paragraphs; never raw HTML
media { type: 'image' | 'video', src, alt? }
placement Placement 'auto', a side, or side-start / side-end
arrow ArrowStyle per-step override
spotlight SpotlightOptions per-step override
overlay OverlayOptions per-step override
advance Advance how the step completes; see Steps
interaction 'block' | 'allow' default: allow for click/input advances, else block
condition Condition skip the step when false
onMissing 'skip' | 'wait' | 'abort' default skip
waitFor number ms to wait for the target; wait defaults to 3000
route string path pattern the step belongs to
buttons { back?, next?, skip?, close? } hide individual buttons
scroll ScrollOptions per-step override
meta Record<string, unknown>

A string is a CSS selector. An object is the portable form:

Field Type Notes
name string resolves to [data-docent="name"] on the web, a test id on native
selectors string[] CSS fallbacks tried in order
native string native id when it differs from name
within string restrict the search to a container
nth number pick among multiple matches
'button' // Next button (default)
{ on: 'click', target? } // user clicks the target
{ on: 'input', target?, match? } // typed value matches a regex
{ on: 'event', name } // controller.notify(name)
{ on: 'element', target } // an element appears
{ on: 'delay', ms }
{ type: 'manual' }
{ type: 'auto', delay? }
{ type: 'route', pattern, delay? }
{ type: 'element', target, delay? }
{ type: 'event', name }
{ type: 'beacon', target?, open?, label? } // target defaults to the first step's; open: 'click' | 'hover'
{ type: 'trait', key, op, value? } // op: eq neq gt gte lt lte in nin contains exists missing
{ type: 'route', pattern }
{ type: 'element', target, exists? }
{ type: 'tour', id, state } // not-started | in-progress | completed | skipped
{ type: 'all' | 'any', conditions }
{ type: 'not', condition }
{ type: 'custom', name, args? } // registered with the `custom` option

Sizes and times accept a CSS string or a number (radius: 12 is 12px, duration: 180 is 180ms). Tokens map to --docent-* custom properties: background, foreground, muted, accent, accentForeground, radius, shadow, font, width, padding, overlay, overlayOpacity, duration, zIndex, connector, ring, beacon (defaults to accent). All are CSS strings.

type ArrowStyle = 'caret' | 'none' | 'line' | 'dashed' | 'dotted' | 'curve' | 'curve-dashed'
| 'squiggle' | 'loop' | 'elbow' | 'sketch' | 'pin'
type ProgressStyle = 'meter' | 'count' | 'ticks' | 'dots' | 'none'
type SpotlightShape = 'rounded' | 'rect' | 'pill' | 'circle'
type SpotlightRing = 'hairline' | 'none' | 'glow' | 'pulse' | 'dashed' | 'solid'
type OverlayStyle = 'dim' | 'blur' | 'vignette' | 'none'
type BeaconStyle = 'pulse' | 'dot' | 'ring' | 'badge' | 'none'
type BeaconPosition = 'top-left' | 'top' | 'top-right' | 'right' | 'bottom-right'
| 'bottom' | 'bottom-left' | 'left' | 'center'

validateTour reports anything that does not match this page, in plain words, with the value that was probably meant:

import { formatIssues, validateTour } from '@docentjs/dom/validate'
const issues = validateTour(tour)
// [{ level: 'error', path: 'steps[0].arrow',
// message: '"curvy" is not one of: caret, none, line, …', suggestion: 'curve' }]
console.log(formatIssues(issues))

It catches unknown values, missing and misspelled fields, wrong types, out-of-range numbers and duplicate step ids. It says nothing about whether a step’s target is on the page, which the devtools Audit tab checks.

This already runs for you in development. createTour and createDocent check each tour once and warn in the console. Production builds contain neither the check nor the code behind it, as long as your bundler sets process.env.NODE_ENV, which Vite, webpack, Next.js, Rollup and esbuild setups do.

Terminal window
npx @docentjs/cli validate "tours/*.json"

Checks tour files without a browser, which suits CI and tools that write tours. It exits 1 when anything is wrong. --json reports for other tools to read, and docent schema tour-schema.json writes the schema to a file for offline use.

tours/welcome.json
✗ steps[0].arrow: "curvy" is not one of: caret, none, line, … Did you mean "curve"?
! steps[1].titel: Unknown field, which Docent will ignore. Did you mean "title"?
1 file checked: 1 error, 1 warning.

Besides the schema check, Docent warns in development when something would otherwise fail in silence:

  • starting a tour id that does not exist, listing the ids it knows,
  • a step skipped because its target is not on the page, naming the step and the target.

The same rule applies: production builds contain neither the checks nor their messages.

The validate function is exported from @docentjs/core/validate and re-exported from @docentjs/dom/validate. isValidTour(tour) returns a boolean, validateTheme(theme) runs the same checks on a theme file, and tourJsonSchema() returns the JSON Schema published at docentjs.dev/schema/tour-v1.json.

The documentation is also published as plain text for AI tools: see Using Docent with AI.