Migrating
From driver.js
driver.js is imperative and vanilla. tourkit is declarative and React-only, so the shape changes, but the concepts line up.
| driver.js | tourkit |
|---|---|
driver({ steps }) then .drive() |
<TourProvider tours={[tour]}> then start(id) |
element |
target |
popover.title / popover.description |
title / body |
popover.side |
placement |
stagePadding |
theme.spotlight.padding or per-step padding |
stageRadius |
theme.spotlight.radius or per-step radius |
overlayColor / overlayOpacity |
theme.scrim.color / theme.scrim.opacity |
disableActiveInteraction: true |
interaction: "block" (the default) |
advanceOnClick |
interaction: "advance-on-press" |
waitForElement: 500 |
gateTimeoutMs: 500, or a gate for anything conditional |
skipMissingElement |
onGateTimeout: "skip" (the default) |
smoothScroll |
scroll: { behavior: "smooth" } (the default) |
onHighlighted |
onEvent with step:enter |
onDestroyed |
onEvent with tour:complete or tour:abort |
popoverClass |
the Card slot, or @tourkit/react/unstyled |
What you gain: the same steps file runs on React Native, steps can be conditional, a tour resumes where the user left it, and gates can wait on your data rather than a fixed timeout.
What you give up: it is React-only, and it is bigger than 5KB.
From react-joyride
| react-joyride | tourkit |
|---|---|
<Joyride steps run /> |
<TourProvider tours> plus start(id) |
step.target (selector) |
target, which also accepts a data-tour-id or a registered id |
step.content |
body |
step.placement |
placement |
step.disableBeacon |
not applicable, there are no beacons |
spotlightClicks |
interaction: "passthrough" |
disableOverlayClose |
the default; the scrim is never a close button |
styles |
theme, or the Card slot |
tooltipComponent |
components.Card |
callback with EVENTS / STATUS |
onEvent |
stepIndex + run for control |
useTour() controls, or useTourState() headless |
portalElement |
container |
Two joyride problems that do not exist here: targets inside scroll containers are tracked through
Floating UI's autoUpdate, and the overlay carries a z-index so a modal cannot render on top of
it.
From react-native-copilot or rn-tourguide
| copilot / tourguide | tourkit |
|---|---|
<CopilotProvider> / <TourGuideProvider> |
<TourProvider> |
<CopilotStep> / <TourGuideZone> |
<TourTarget> plus a step in the config |
| step order declared on the wrapper | order declared in the steps array |
text on the wrapper |
title and body on the step |
shape="circle" |
radius: 999, or radius: "auto" to read it from the element |
tooltipComponent |
components.Card |
copilotEvents / eventEmitter |
onEvent |
useCopilot() / useTourGuideController() |
useTour() |
tourKey for multiple tours |
multiple entries in tours |
The structural difference: those libraries put the content on the wrapper, so the tour is
scattered across your screens. Here the wrapper only says "this is target post-ride" and all
the copy and ordering lives in one file. That file is also what runs on web.
Both of those packages were last published in 2024 and neither is verified against a current Expo and New Architecture stack.