Skip to main content
userTourKit
Codemods

from-shepherd

Migrate a Shepherd.js v10+ codebase to Tour Kit with the tour-kit-migrate --from shepherd codemod.

domidex01Published

Rewrites the new Shepherd.Tour({...}) + .addStep({...}) + .start() imperative chain into a single Tour Kit tour-shaped object literal.

Run

npx tour-kit-migrate --from shepherd --dry-run ./src
npx tour-kit-migrate --from shepherd ./src

Run with --dry-run to preview the diff without writing. Run with --print to write transformed source to stdout. Add --verbose to log every file action.

What it changes

Shepherd.jsTour Kit
import Shepherd from 'shepherd.js'import { TourProvider } from '@tour-kit/react'
import { Tour } from 'shepherd.js'import { TourProvider } from '@tour-kit/react'
new Shepherd.Tour({...}){ id: 'migrated-tour', steps: [...] } literal + // TODO: to register at <TourProvider>
.addStep({ id, text, attachTo }) chainmerged into the steps: [...] array
attachTo.element (selector)target
attachTo.on (placement)placement
Step.textcontent
tour.start()// TODO:useTour().start()
tour.cancel()// TODO:useTour().skip()
tour.complete()// TODO:useTour().complete()
tour.next() / .back() / .show()// TODO:useTour().next() / .prev() / .goTo()

What it does NOT handle

  • buttons arrays. Shepherd's button array is open-ended; the codemod drops it and leaves a // TODO:. Tour Kit ships fixed <TourCard> slots (Next / Prev / Skip / Close) — wire any custom button action through the card's children.
  • classes, scrollTo, when, advanceOn, beforeShowPromise. Each is preserved as a // TODO: linking to the matching anchor in the Shepherd.js migration guide.
  • Dynamic attachTo or addStep arguments (spread/variable, not an inline object) — the codemod can't inline the shape, so it leaves a // TODO:.
  • Multiple parallel Tour instances. Tour Kit's multi-tour registry handles this differently — see <TourProvider>.

Example

Before:

import Shepherd from 'shepherd.js'

const tour = new Shepherd.Tour({
  defaultStepOptions: { scrollTo: true, cancelIcon: { enabled: true } },
})

tour.addStep({
  id: 'welcome',
  text: 'Welcome!',
  attachTo: { element: '#header', on: 'bottom' },
})

tour.addStep({
  id: 'cta',
  text: 'Click here to begin.',
  attachTo: { element: '#cta-button', on: 'top' },
})

tour.start()

After codemod:

import { TourProvider } from '@tour-kit/react'

// TODO: register this tour at a <TourProvider> ancestor — see
// /docs/migration/shepherd#tour-constructor
const tour = {
  id: 'migrated-tour',
  steps: [
    { id: 'welcome', content: 'Welcome!', target: '#header', placement: 'bottom' },
    { id: 'cta', content: 'Click here to begin.', target: '#cta-button', placement: 'top' },
  ],
}

// TODO: the migrated tour has no .start() — call useTour().start() from a
// descendant of the <TourProvider> that registers it.
// /docs/migration/shepherd#start

Wire it up by hand:

import { TourProvider, useTour } from '@tour-kit/react'

function App({ children }) {
  return <TourProvider tours={[tour]}>{children}</TourProvider>
}

function StartButton() {
  const { start } = useTour()
  return <button onClick={() => start()}>Take the tour</button>
}

See also

Free & open source

Ship onboarding, not config.

npm i @tour-kit/core is MIT and free. The Pro packages work unlicensed too — a one-time $99 license removes the production watermark when you ship.

MIT-licensed — no signup, no credit card. Pay once, only when you ship.