Installation
Packages, provider, and your first tour.
Packages
npm i @cairnkit/core @cairnkit/react @cairnkit/ui @cairnkit/next
npm i -D @cairnkit/cliOr let the CLI do it. It detects your framework, writes those files into src/walkthrough/, and prints the three steps it deliberately leaves to you:
npx cairnkit init
--dir <path> where the files go. Defaults to src/walkthrough
--dry-run print the whole plan and write nothingIt reads your framework, whether source sits under src/, your tsconfig path alias and your package manager, then writes an anchor registry, a starter flow, the type registry and a provider. It never overwrites a file, and running it twice does nothing.
JavaScript projects get JavaScript. You lose the type registry, which is the part that turns a rename into a compile error, and init says so rather than pretending otherwise.
@cairnkit/reactpulls incore— you rarely install it directly.@cairnkit/uiis the prebuilt overlay. Skip it if you are building your own.@cairnkit/nextis the router adapter. Use your own for other routers.
Supported versions
| Prop | Type | Default | Description |
|---|---|---|---|
React | 18 · 19 | — | Verified by building a real app on both. Needs useSyncExternalStore, so 18 is the floor. |
Next.js | 14 · 15 · 16 | — | App Router and Pages Router. Verified against 14.2, 15.5 and 16.3. |
Vite / react-router | any | — | Router access is a ten-line adapter — see the React page. |
Node (for the CLI) | >= 18 | — | cairnkit check and the audit helper. |
Register your types
Optional, but it is what turns every anchor and flow id into a checked literal instead of a string.
import type { anchors } from "./anchors";
declare module "@cairnkit/core" {
interface CairnRegister {
anchors: typeof anchors;
flowIds: "upgrade-plan";
events: "plan:upgraded";
}
}Mount the provider
"use client";
import type { ReactNode } from "react";
import { CairnProvider } from "@cairnkit/react";
import { useAppRouterAdapter } from "@cairnkit/next";
import { CairnOverlay, TourLauncher } from "@cairnkit/ui";
import "@cairnkit/ui/styles.css";
import { upgradeFlow } from "./flows";
export function Providers({ children }: { children: ReactNode }) {
return (
<CairnProvider flows={[upgradeFlow]} router={useAppRouterAdapter()}>
{children}
<CairnOverlay />
<TourLauncher flowId="upgrade-plan" />
</CairnProvider>
);
}Then wrap your app with it. The provider has to be an ancestor of every anchor — rendered as a sibling it supplies context to nothing, and every useTour() below throws.
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Wire up the check
"scripts": {
"lint": "eslint . && cairnkit check src"
}