Installation

Packages, provider, and your first tour.

Packages

npm i @cairnkit/core @cairnkit/react @cairnkit/ui @cairnkit/next
npm i -D @cairnkit/cli

Or 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 nothing

It 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/react pulls in core — you rarely install it directly.
  • @cairnkit/ui is the prebuilt overlay. Skip it if you are building your own.
  • @cairnkit/next is the router adapter. Use your own for other routers.

Supported versions

PropTypeDefaultDescription
React18 · 19Verified by building a real app on both. Needs useSyncExternalStore, so 18 is the floor.
Next.js14 · 15 · 16App Router and Pages Router. Verified against 14.2, 15.5 and 16.3.
Vite / react-routeranyRouter access is a ten-line adapter — see the React page.
Node (for the CLI)>= 18cairnkit 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.

app/cairn.d.ts
import type { anchors } from "./anchors";

declare module "@cairnkit/core" {
  interface CairnRegister {
    anchors: typeof anchors;
    flowIds: "upgrade-plan";
    events: "plan:upgraded";
  }
}

Mount the provider

app/providers.tsx
"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.

app/layout.tsx
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

package.json
"scripts": {
  "lint": "eslint . && cairnkit check src"
}