Provider reference
TourProvider owns the engine and renders the overlay. Mount it once, above everything the tour
can point at.
<TourProvider tours={[onboarding]} context={{ plan: "pro" }}>
<App />
</TourProvider>You do not mount the overlay separately. The provider renders it after children, which puts it
on top.
Annotate the context type
Write <TourProvider<AppContext> ...> when your steps use when, gate, onEnter or
onAdvance. Without the annotation TypeScript infers the context type from the object literal
you pass, which widens "pro" to string, and your predicates then fail to typecheck against
the inferred type.
<TourProvider<AppContext> tours={[onboarding]} context={{ plan: "pro" }}>Props on both platforms
| Prop | Type | Default | Meaning |
|---|---|---|---|
tours |
TourConfig<Ctx>[] |
required | Every tour the app can start. |
context |
Ctx |
undefined |
Passed to when, gate, onEnter and onAdvance. Your type, never inspected. |
theme |
ThemeOverride |
Lowest precedence. Tour and step themes override it. | |
storage |
StorageAdapter |
localStorage on web, none on native |
Where completion and resume position are saved. |
nav |
NavAdapter |
Needed only for steps that declare a route. |
|
onEvent |
(name, event) => void |
The whole analytics story. See below. | |
components |
Partial<Slots> |
Replace the card, backdrop or progress indicator. | |
children |
ReactNode |
required | Your app. |
Web only
| Prop | Type | Default | Meaning |
|---|---|---|---|
container |
Element | null |
document.body |
Where the overlay is portalled. |
styled |
boolean |
true |
false drops every cosmetic style and keeps only the structural ones. Importing from @tourkit/react/unstyled sets it for you. |
React Native only
| Prop | Type | Default | Meaning |
|---|---|---|---|
insets |
{ top, bottom, left, right } |
all zero | Safe-area insets. Pass useSafeAreaInsets() if you use react-native-safe-area-context. |
scrollRef |
RefObject<ScrollView | null> |
Lets the tour scroll an off-screen target into view. | |
debug |
boolean |
false |
Reserved for the debug overlay. |
insets is a prop rather than a dependency on purpose. One line of wiring beats forcing a native
package on everyone.
Events
<TourProvider onEvent={(name, event) => analytics.track(name, event)} tours={tours}>Every event carries { tourId, stepId, stepIndex, total }. stepId is null on the two
tour-level completion events.
| Event | When |
|---|---|
tour:start |
A tour begins from the first step. |
tour:resume |
A tour begins from a saved position. |
step:enter |
A step became active, meaning its gate passed. |
step:exit |
A step is being left through next, back or skip. |
step:skip |
A step was skipped, either by the user or by a failed gate. |
target:timeout |
A gate did not resolve in time. Always paired with whatever the policy did next. |
tour:complete |
The last step finished. |
tour:abort |
The tour was stopped early. |
There is no analytics dependency and nothing is sent anywhere. Wire this to whatever you already use.
Controls
const { start, stop, next, prev, skip, running } = useTour();| Function | Behaviour |
|---|---|
start(tourId) |
Begins a tour, resuming from a saved position when one matches. |
stop() |
Ends it and records the outcome as skipped. |
next() |
Runs onAdvance, then moves on. Ignored while a step is still resolving. |
prev() |
Goes back one visible step. Ignored on the first. |
skip() |
Moves on without running onAdvance. |
running |
True from start until the tour ends, including while a gate is pending. |
Changing props mid-tour
theme, tours, storage, nav and onEvent can all change while a tour is running and the
engine follows without restarting. A dark-mode toggle does not interrupt a tour.