API reference
Every export.
@cairnkit/core
Framework-free engine. Zero runtime dependencies.
| Prop | Type | Default | Description |
|---|---|---|---|
defineAnchors(registry) | <T>(T) => T | — | Declares the anchor registry. Identity at runtime; exists for type inference. |
anchor(id) | (id) => props | — | Props object to spread onto an element. |
anchorSelector(id) | (id) => string | — | The CSS selector for an anchor, escaped. |
resolveAnchor(id, doc?) | (id) => HTMLElement | null | — | Finds the visible element for an anchor. Returns null on the server. |
defineFlow(flow) | (TourFlow) => TourFlow | — | Declares a flow. |
createFlowRegistry(flows) | (TourFlow[]) => Registry | — | Builds an id → flow map. |
getFlow(registry, id) | (...) => TourFlow | null | — | Looks a flow up by id. |
resolveResumeStep(flow, path, i) | (...) => number | null | — | Where to resume when the user is ahead. Pure, testable. |
decideForRoute(flow, path, i) | (...) => RouteDecision | — | handoff / pause / resume / none, in that order. |
createTourStore(options) | (...) => TourStore | — | Observable store, useSyncExternalStore-ready. |
createActionRegistry() | () => ActionRegistry | — | Backs ctx.run in step hooks. One per provider — never module-level, which would leak across SSR requests. |
localStoragePersist(key?) | (key?) => PersistAdapter | — | Default persistence. Progress must survive a page transition. |
emitTourEvent(name) | (name) => void | — | Signal from your app that a step is waiting on. |
onTourEvent(name, fn) | (...) => () => void | — | Subscribe. Returns an unsubscribe. |
readRect / rectsEqual | helpers | — | Measure and compare a target rect. |
watchForAnchor(id, ms, cb) | (...) => () => void | — | Wait for a late-mounting anchor. |
scrollAnchorIntoView(el) | (el) => void | — | Reduced-motion aware. |
Key types
TourFlow · TourStep · AdvanceRule · Placement · AnchorId
CairnRegister · RegisteredAnchor · RegisteredFlowId · RegisteredEvent · RegisteredAction
StepContext · ActionRegistry · TourAction
CairnEvent · CairnEventHandler · TargetRect · TourStore · PersistAdapter
TourDismissReason · TourExitReasonEvents
One onEvent callback receives every signal a tour emits. Point it at your analytics, or at @cairnkit/cloud.
| Prop | Type | Default | Description |
|---|---|---|---|
flow_started | flowId, version | — | A tour began. |
step_viewed | flowId, stepIndex, anchor | — | One per step, however many times it re-renders. |
flow_completed | flowId, version | — | Reached the end. |
flow_dismissed | flowId, version, stepIndex, reason | — | Left early. reason is 'skipped' (the Skip button), 'closed' (the X) or 'escape'. Three different intents kept apart because they argue for opposite fixes: a step people skip is unwanted, a step people close is usually one whose card covers what they were trying to see. |
flow_handoff | fromFlowId, toFlowId, pathname | — | One tour handed off to another. |
anchor_missing | flowId, stepIndex, anchor, pathname, optional? | — | A step pointed at UI that was not on the page — what cairnkit check catches before release, seen from production. `optional: true` is set when the step declared itself optional, so an expected absence can be told from a break: count these as breakage and a guide skipping a step exactly as intended reads as a broken anchor. Absent, never false, on a required step. |
step_feedback | flowId, stepIndex, clear, note | — | A reader said whether the step made sense. |
Every event is { name, props }, so the callback maps onto an analytics client directly. Nothing below needs an account with us.
<CairnProvider
flows={flows}
onEvent={(event) => posthog.capture(event.name, event.props)}
>
{children}
</CairnProvider>Segment, Amplitude, Mixpanel and a plain fetch to your own collector take the same shape. To send to a tool and to cloud, call both:
import { sendToCloud } from "@cairnkit/cloud";
// Calls sharing a key share one transport, so an inline call during render is
// also correct. Named here only because it is used alongside another handler.
const toCloud = sendToCloud({ key: process.env.NEXT_PUBLIC_CAIRNKIT_KEY! });
<CairnProvider
flows={flows}
onEvent={(event) => {
posthog.capture(event.name, event.props);
toCloud(event);
}}
>@cairnkit/react
Headless bindings. No styling.
| Prop | Type | Default | Description |
|---|---|---|---|
<CairnProvider> | component | — | Mounts the runtime. Props: flows, router, translate, onEvent, onNotice, store, mobileBreakpoint. |
useTour() | hook | — | The controller: flow, step, rect, element, status, advance, back, skip, start, stop. Mount it once: it binds listeners and fires lifecycle hooks. |
useActiveTour() | hook | — | Read-only view: flow, stepIndex, pathname, decision, isPaused. No side effects, so it is safe beside useTour. |
useStartTour() | hook | — | Just the starter, without the driver. What a custom launcher wants. |
useTourScope(scope) | hook | — | Declares which tab or stage is in front, for flows that carry a scope. |
useTourState(selector) | hook | — | Subscribe to a slice of store state. |
useStepCopy(flow, step) | hook | — | Resolves title and body, inline or via translate. |
useTourDeepLink(param?) | hook | — | Starts a flow from ?tour=. Each value honoured once. |
useCairn() | hook | — | Raw context: flows, router, store. |
useTourAction(name, fn) | hook | — | Publishes an action a step can call via ctx.run, for as long as the component is mounted. |
useAnchorTarget(id, opts) | hook | — | Resolves one anchor to a live element and rect, and reports status while it waits. What useTour drives internally; useful when building your own overlay. Options: waitForMs, enabled, resetKey. |
memoryRouter | adapter | — | For an app with no router, and the default in tests. Reads window.location.pathname and navigates with window.location.assign. Prefer it over hand-writing an adapter that returns a fixed path. |
<TourAnchor id> | component | — | Escape hatch for components that swallow props. |
Also re-exports defineFlow, defineAnchors, anchor and the flow types from @cairnkit/core, so authoring a tour needs one import rather than two. Note that augmenting CairnRegister still has to target @cairnkit/core — an interface cannot be merged through a re-export — which is why core is a direct dependency in the install command.
@cairnkit/ui
Prebuilt overlay. Plain prefixed CSS, light and dark.
| Prop | Type | Default | Description |
|---|---|---|---|
<CairnOverlay> | component | — | Spotlight plus tooltip card. Props: labels, mobileBreakpoint, onNotice. |
<TourLauncher> | component | — | The launcher. Props: flowId, label, position, pulse, icon, className. |
<Spotlight> | component | — | Scrim and cutout only. |
<StepCard> | component | — | The card only. |
<Launcher> | component | — | Presentational launcher, unbound to a flow. |
<ProgressRail> | component | — | Segmented step progress. |
<CairnNoticeBar> | component | — | Renders the notices the provider raises, such as an anchor that never appeared. Props: labels. |
<Button> | component | — | The card's own button, exported so a custom card can match it. |
<IconButton> | component | — | Icon-only variant, used for dismiss. |
@cairnkit/ui/styles.css | stylesheet | — | Import once. Required for the prebuilt components. |
@cairnkit/next
| Prop | Type | Default | Description |
|---|---|---|---|
useAppRouterAdapter() | hook | — | App Router adapter. |
usePagesRouterAdapter() | hook | — | Pages Router adapter. Reads router.asPath, not router.pathname, so it reports the resolved path rather than the route pattern. |
appRouterAdapter() | function | — | Non-hook form, kept for callers that cannot use a hook. Prefer useAppRouterAdapter: this one calls useRouter inside navigate, which breaks the rules of hooks. |
@cairnkit/cloud
Optional. Reports the events the library already emits to cairnkit cloud. Nothing else in the library depends on it.
| Prop | Type | Default | Description |
|---|---|---|---|
sendToCloud(opts) | function | — | Returns an onEvent handler for CairnProvider. Options: key, and optionally endpoint. Batches and sends with sendBeacon. |
reportFlows(opts) | function | — | Sends the flow and step copy once per locale, so the dashboard can name a step instead of numbering it. Deduped on a fingerprint of the content, so a locale switch reports again. Options: key, flows, locale. |
currentSession() | function | — | The current session id, from localStorage, rotating after an idle gap. Exported because a custom transport needs the same id the built-in one uses. |
@cairnkit/cli
| Prop | Type | Default | Description |
|---|---|---|---|
cairnkit check <dir> | command | — | Static drift check. Exits 1 on any finding. |
cairnkit status <dir> | command | — | Describe every anchor: whether an element carries it, how it was applied, where it was declared, and which flows point at it. Always exits 0 — describing is not judging. |
--json | flag | — | On check or status. Writes exactly one JSON object to stdout and every human-facing message to stderr, so the output pipes straight into a parser. The payload carries a version field. |
auditFlow(page, opts) | function | — | Drives one flow in a browser and reports per-step. |
auditFlows(page, list) | function | — | Several flows; throws one readable error if any fail. |