# @inklu/tour
> Accessible, headless product tours for React: a spotlight overlay, resilient target tracking and full control over the card.
- Install: `npm install @inklu/tour`
- Agent skill: `npx skills add nkurunziza-saddy/inklu --skill inklu-tour`
## Docs
- [Full reference](https://inklu.saddy.me/tour/llms-full.txt): every export, option and pattern, as Markdown
- [Documentation](https://inklu.saddy.me/tour): Defining tours, targeting steps and the API
## Optional
- [Source](https://github.com/nkurunziza-saddy/inklu/tree/main/packages/tour)
- [npm](https://www.npmjs.com/package/@inklu/tour)
## Setup prompt
Set up an accessible product tour using the `@inklu/tour` package.
Do not invent APIs. If something here is ambiguous, read https://inklu.saddy.me/tour
before guessing.
### 1. Install
`npm install @inklu/tour`
Peers: `react` and `react-dom` 18 or 19.
### 2. Define your tours
Create a `TourConfig[]` array. Each config has an `id` and a `steps` array.
```tsx
import { type TourConfig, TourProvider, useTour } from "@inklu/tour";
const tours: TourConfig[] = [
{
id: "onboarding-tour",
steps: [
{
id: "step-welcome",
target: "#welcome-header",
placement: "bottom",
meta: {
title: "Welcome aboard!",
content: "Let's take a quick tour around the dashboard.",
},
},
],
},
];
```
### 3. Add the provider
Wrap your app (or the subtree that contains tour targets) with `TourProvider`:
```tsx
// app/providers.tsx — must be a Client Component
"use client";
import { TourProvider, type TourConfig } from "@inklu/tour";
import { useRouter } from "next/navigation";
const tours: TourConfig[] = [/* ... */];
export function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
router.push(route)}>
{children}
);
}
```
`onNavigate` is required for cross-page tours.
### 4. Trigger a tour
Call `startTour` from the `useTour` hook anywhere inside the provider:
```tsx
"use client";
import { useTour } from "@inklu/tour";
export function StartButton() {
const { startTour } = useTour();
return ;
}
```
### 5. Target elements
Prefer `data-tour-step` attributes over CSS selectors — they survive refactors:
```tsx
```
For async-loaded elements, use a target object with a timeout and strategy:
```tsx
target: { selector: ".async-loaded-modal", timeout: 3000, strategy: "skip" }
```
### 6. Composition (optional)
Replace the default card UI with `TourRoot` + primitives from `@inklu/tour`:
`TourSpotlight`, `TourCard`, `TourArrow`, `TourNextButton`,
`TourPreviousButton`, `TourCloseButton`, `useTourContext`.
Always wire `labelId` and `descriptionId` from `useTourContext` to the
corresponding elements in your card to maintain accessibility.
### Constraints
- Never put `TourProvider` in a Server Component file.
- Do not use dot notation like `` — import sub-components explicitly.
- The `useTour` hook throws if called outside a `TourProvider`.
### Before you finish
Confirm every `target` selector or `data-tour-step` value resolves to a real
element, and that the tour `id` passed to `startTour` matches a config id.