API reference
Every public export, by package. The pages linked from each table explain when to reach for a thing; this page exists so you can tell whether it exists at all.
Most apps use eight of these: TourProvider, TourTarget or data-tour-id, useTour,
TourConfig, TourStep, createNavAdapter, readRecord and clearRecord. The rest are there
for custom renderers, tooling and tests.
Entry points
| Import path | Contains |
|---|---|
@tourkit/core |
The engine and the types. No React, no DOM, no dependencies. |
@tourkit/react |
The DOM renderer, the hooks, the recorder. |
@tourkit/react/unstyled |
The same, with styled={false} applied. See Customization. |
@tourkit/native |
The React Native renderer. |
@tourkit/ai |
The client half of question-to-tour. Safe in a browser bundle. |
@tourkit/ai/server |
The server half. Holds your model key. |
@tourkit/react and @tourkit/native re-export the types and the four helper functions you
actually need from core, so a web-only app imports from one package.
@tourkit/core
Engine
| Export | Signature | Notes |
|---|---|---|
TourEngine |
new TourEngine<Ctx>(options: EngineOptions<Ctx>) |
The whole state machine. TourProvider creates one for you. Build one directly only for a renderer of your own. |
EngineOptions<Ctx> |
{ tours, context, theme?, storage?, nav?, onEvent? } |
|
EngineSnapshot<Ctx> |
{ status, tourId, stepIndex, step, steps, total, activeTarget, rects, theme, dismissible } |
Immutable. A new object on every change, which is what useSyncExternalStore needs. |
Methods on TourEngine:
| Method | Signature | Behaviour |
|---|---|---|
subscribe |
(listener: () => void) => () => void |
Returns the unsubscribe function. |
getSnapshot |
() => EngineSnapshot<Ctx> |
Bound, so it can be passed by reference. |
setContext |
(context: Ctx) => void |
Re-filters when predicates immediately, including mid-tour. |
setOptions |
(patch: Partial<Omit<EngineOptions<Ctx>, "context">>) => void |
Everything except context can change while a tour runs. |
start |
(tour: string | TourConfig<Ctx>) => Promise<void> |
An id starts a registered tour. A config object starts an ad-hoc one that is never registered. |
stop |
() => Promise<void> |
Records the outcome as skipped. |
advance |
() => Promise<void> |
Runs onAdvance, then moves on. Ignored unless the status is active. |
back |
() => Promise<void> |
Ignored on the first step. |
skip |
() => Promise<void> |
Moves on without running onAdvance. |
setRect |
(target: string, rect: Rect) => void |
How a renderer reports a measurement. Ignored when the rect has not moved by half a pixel. |
clearRect |
(target: string) => void |
Persistence
| Export | Signature |
|---|---|
storageKey |
<Ctx>(config: TourConfig<Ctx>) => string, returning tour:{id}:v{version} |
readRecord |
<Ctx>(storage, config) => Promise<PersistedTour | null> |
writeRecord |
<Ctx>(storage, config, record: PersistedTour) => Promise<void> |
clearRecord |
<Ctx>(storage, config) => Promise<void> |
All four accept undefined for storage and all four swallow storage errors, so a browser in
private mode or a full quota never throws into your app. A malformed record reads back as null.
Navigation
| Export | Signature |
|---|---|
createNavAdapter |
({ pathname, navigate, match? }: NavAdapterOptions) => NavAdapter |
routeMatches |
(pathname: string, route: string, match: RouteMatch) => boolean |
RouteMatch |
"exact" | "prefix" | "includes", defaulting to "exact" |
All three strategies ignore case, a trailing slash, a query string and a hash. See Navigation.
Theme
| Export | Signature |
|---|---|
defaultTheme |
Theme. Every token, fully resolved. |
mergeTheme |
(...overrides: (ThemeOverride | undefined)[]) => Theme. Key-by-key, left to right. |
Types: Theme, ThemeOverride, TextStyle, FontWeight, ProgressStyle, TextContrast. Every
token is listed in the Theme reference.
Contrast
| Export | Signature | Notes |
|---|---|---|
luminance |
(color: string) => number | null |
null for anything that is not a hex or rgb() string. |
contrastRatio |
(a: string, b: string) => number | null |
WCAG ratio, 1 to 21. |
isDark |
(color: string) => boolean |
Luminance below 0.45. An unparseable colour is treated as light. |
These back text.contrast: "auto". They are exported because a custom card needs the same
answers.
Step resolution
| Export | Signature |
|---|---|
visibleSteps |
<Ctx>(config: TourConfig<Ctx>, context: Ctx) => TourStep<Ctx>[] |
stepAt |
<Ctx>(steps: TourStep<Ctx>[], index: number) => TourStep<Ctx> | null |
indexOfStep |
<Ctx>(steps: TourStep<Ctx>[], stepId: string) => number |
Geometry
| Export | Signature |
|---|---|
Rect |
{ x: number; y: number; width: number; height: number } |
rectsEqual |
(a: Rect | null, b: Rect | null, epsilon = 0.5) => boolean |
Types
Align, EventHandler, Fingerprint, GateArgs, GateTimeoutPolicy, Interaction,
NavAdapter, PersistedTour, Placement, ScrollOptions, StorageAdapter,
TargetDescriptor, TargetManifest, TourConfig, TourEvent, TourEventName, TourOutcome,
TourStatus, TourStep.
TourConfig and TourStep are documented field by field in the
Step reference. The four small ones:
type StorageAdapter = {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
remove(key: string): Promise<void>;
};
type NavAdapter = {
getRoute(): string;
matches(route: string): boolean;
navigate(route: string): void | Promise<void>;
};
type TourEvent = { tourId: string; stepId: string | null; stepIndex: number; total: number };
type PersistedTour = { outcome: TourOutcome; stepId: string; updatedAt: number };@tourkit/react
Components
| Export | Notes |
|---|---|
TourProvider |
Mount once at the root. Props in the Provider reference. |
TourHost |
The overlay. TourProvider renders it after children; you never mount it yourself. |
TourHint |
A standalone dot with a popover, independent of any tour. See Hints. |
TourRecorder |
The recorder panel. See Recording a tour. |
CoachCard, Overlay, ProgressDots |
The three default slots, exported so a custom slot can wrap one rather than replace it. |
type TourRecorderProps = {
name?: string; // "recorded". Becomes the generated file name.
endpoint?: string; // "http://127.0.0.1:5178/record"
autoShow?: boolean; // true. Hides the panel unless `tourkit record` answers.
onFinish?: (recording: Recording) => void;
};Hooks
| Hook | Returns |
|---|---|
useTour() |
{ running, start, stop, next, prev, skip }. The controls, with no re-render on step changes. running is a boolean, so it only re-renders when the tour starts or ends. |
useTourState() |
The whole snapshot plus rect, next, prev, skip, stop. The headless path. |
useTourSnapshot() |
EngineSnapshot<Ctx>. Re-renders on every engine change. |
useTourSelector(select) |
T. Re-renders only when the selected value changes. |
useTourTarget(id) |
A ref to attach to an element, registering it under id. The alternative to data-tour-id. |
useTargetManifest() |
() => TargetManifest. Every registered target plus every data-tour-id in the document, with labels. Feeds @tourkit/ai. |
useTourContext() |
{ engine, components, registry, container, styled, classNames, storage, openHint, setOpenHint }. Throws outside a provider. |
useEngine() |
The TourEngine itself. |
useTourSelector reads through a callback, so wrap the selector in useCallback or define it
outside the component. An inline arrow re-subscribes on every render.
Storage
| Export | Notes |
|---|---|
browserStorage() |
localStorage behind try/catch and an optional-chained global. The provider's default on web. |
memoryStorage() |
A Map. Use it in tests so one case cannot leak into the next. |
DOM utilities
| Export | Signature |
|---|---|
resolveTarget |
(target: string, registry: Map<string, Element>) => Element | null |
resolveWithFingerprint |
(target, registry, fingerprint?) => { element: Element | null; healed: boolean } |
scrollSettings |
(scroll: boolean | ScrollOptions | undefined) => { enabled, block, behavior } |
scrollIntoViewIfNeeded |
(element, block, behavior) => void. A no-op when the element is already fully in view. |
toFloatingPlacement |
(placement: Placement, align?: Align) => Placement in Floating UI's vocabulary |
holePathData |
(vw, vh, x, y, width, height, cornerRadius) => string. An SVG path: the viewport rectangle, then the rounded hole. |
holeClipPath |
The same wrapped as path(evenodd, "..."), ready for a CSS clip-path. |
nextFocusTarget |
(container: HTMLElement, active: Element | null, backwards: boolean) => HTMLElement | null. The focus trap's cycle. |
resolveTarget tries three things in order: an id registered through useTourTarget, a
data-tour-id attribute, then the string as a CSS selector. An invalid selector returns null
rather than throwing.
Fingerprints
| Export | Signature |
|---|---|
buildFingerprint |
(element: Element) => Fingerprint |
healTarget |
(fingerprint: Fingerprint, root?: Document) => Element | null |
scoreCandidate |
(element: Element, fingerprint: Fingerprint) => number |
nearestHeading |
(element: Element) => string | undefined |
The scoring rules and the tie-breaking are in When a target breaks.
Recorder internals
| Export | Signature |
|---|---|
describeElement |
(element, route?, registry?) => RecordedStep |
cssPath |
(element: Element, root?: Document) => string. Prefers data-testid, then a stable id, then the shortest unique ancestor chain, capped at six segments. |
textOf |
(element: Element) => string | undefined. Trimmed, collapsed, capped at 120 characters. |
RecordedStep, Recording |
The wire format tourkit record consumes. |
Slot types
CardProps, BackdropProps, ProgressProps, Slots, CardPlacement, ClassNames. All six are
shown in use in Customization.
@tourkit/native
Everything in @tourkit/react that is not DOM-specific has a counterpart here, with the same
name and the same meaning. What is different:
| Export | Notes |
|---|---|
TourTarget |
{ id, children, radius?, padding?, label?, style? }. Wraps the element instead of tagging it. |
createStorageAdapter(store) |
Turns anything with getItem / setItem / removeItem into a StorageAdapter. AsyncStorage fits as-is. |
createMemoryStorage() |
The native memoryStorage. |
createBlurBackdrop({ MaskedView, BlurView }) |
You pass the modules; the package never imports them. See Customization. |
Spotlight, CoachCard, ProgressDots, TouchShield |
The default slots. |
useTargetMeasure |
The Reanimated frame-callback measurement behind TourTarget. |
Geometry and layout helpers, all pure and all tested: holeMaskPath, resolvePlacement,
cardWidthFor, shieldRegions, padRect, resolvePadding, resolveRadius, scrollOffsetFor,
scrollSettings, pickTarget.
Recorder endpoint helpers: RECORD_PORT (5178), defaultRecordEndpoint, hostFromScriptUrl,
statusUrl.
Native-only types: Insets, ScrollHost, TargetNode, TargetGeometry, SpotlightGeometry.
There is no TourHint on React Native, and no resolveTarget: target is a TourTarget id and
nothing else, because there is no document to query.
@tourkit/ai
Client half. Contains no key handling.
| Export | Signature |
|---|---|
useTourkitAsk |
({ endpoint, getManifest, onTour?, fetchImpl? }) => { ask, reset, status, tour, error } |
askTourkit |
({ endpoint, question, manifest, signal?, fetchImpl?, tourId? }) => Promise<ValidationResult>. The hook without React. |
cacheKey |
(question: string, manifest: TargetManifest) => string. The question, lowercased, plus the sorted target ids. |
validateGenerated |
(raw: unknown, manifest, tourId?) => ValidationResult. The second of the two checks. |
GENERATED_TOUR_ID |
"tourkit-generated". The id a generated tour carries unless you pass tourId. |
AskStatus |
"idle" | "asking" | "ready" | "failed" |
ValidationResult is a discriminated union, so a failure is not an exception:
type ValidationSuccess = { ok: true; tour: TourConfig<unknown> };
type ValidationFailure = {
ok: false;
reason: "malformed" | "unknown-target" | "empty";
detail: string;
};A network error, a non-2xx response and unparseable JSON all come back as malformed, so the
client never throws at a call site that is rendering a help box.
Schemas, exported so you can validate elsewhere: askRequestSchema, generatedTourSchema,
generatedStepSchema, targetDescriptorSchema. Prompts: SYSTEM_PROMPT, DRAFT_SYSTEM_PROMPT,
buildUserPrompt, buildDraftPrompt.
@tourkit/ai/server
Server half. This is the only import that touches your model key.
| Export | Signature |
|---|---|
createTourkitHandler |
({ generate, tourId? }) => (request: Request) => Promise<Response> |
anthropicGenerator |
({ apiKey?, model?, client? }) => TourGenerator. Defaults to claude-opus-5, loads the SDK lazily. |
anthropicDrafter |
({ apiKey?, model?, client? }) => Drafter. Writes step copy from a recording, behind tourkit record --draft. |
TourGenerator |
({ question, manifest }) => Promise<unknown>. Any provider fits. |
The handler answers 405 to anything but POST, 400 on a malformed request, 502 when
generate throws, and 422 with a reason when the result names a target that is not in the
manifest. See Ask and be shown.
@tourkit/cli
A binary, not a library. Nothing imports it at runtime. See the CLI reference.