Wakeline
wakeline for react

React product tours: libraries, working code, and build vs. buy.

You can build a React onboarding tour four ways with open source, or skip the build entirely. Here's all of it honestly: working code for each library, the licensing trap in one of them, what every library leaves you to build yourself, and when buying actually wins.

Libraries and licenses checked August 21, 2026.

The React ecosystem has four serious open-source options for product tours, and they're all genuinely usable — this is not one of those roundups where the open source is a strawman. If your job is a single guided tour, maintained by engineers, shipped once, any of these will do it well.

The honest framing is that the library is about a fifth of the system. Steps and popovers are what libraries give you; targeting, seen-state, analytics, and the ability for non-engineers to edit copy are what production onboarding actually runs on. The roundup covers the first fifth; the second half of this page covers the rest.

the open-source options

The libraries, honestly — with the code.

React Joyridethe React-native default

License:
MIT
Best for:
React teams that want tours as a declarative component

Joyride is the most React-idiomatic of the four: tours are a component, steps are props, and state flows through callbacks like everything else in your tree. Spotlighting, beacons, and step styling are built in, and it handles React's re-renders sensibly.

The trade is that its tour state lives in your app's state management — which is fine for one tour and increasingly your problem for five. Persistence ("has this user seen it?") is entirely yours to build.

import Joyride from "react-joyride";

const steps = [
  {
    target: ".new-project-btn",
    content: "Everything starts here — create your first project.",
  },
  {
    target: ".invite-field",
    content: "Onboarding sticks better with a teammate aboard.",
  },
];

export function OnboardingTour() {
  return <Joyride steps={steps} continuous showSkipButton showProgress />;
}
The canonical Joyride shape: steps as data, tour as a component.

Shepherd.jsthe framework-agnostic heavyweight

License:
MIT (core library)
Best for:
Teams that want fine-grained control and don't mind imperative code

Shepherd is the most capable pure-DOM tour library: modal overlays, rich step options, promise-based hooks around every transition. The react-shepherd wrapper exists, but many teams use the core imperatively — it predates the framework wars and outlived most of them.

The power costs verbosity: every step is a configuration object with explicit buttons and attachment rules, and the styling starts from its own CSS you'll be overriding. Budget real time for making it look native to your product.

import Shepherd from "shepherd.js";
import "shepherd.js/dist/css/shepherd.css";

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

tour.addStep({
  id: "create-project",
  text: "Everything starts here — create your first project.",
  attachTo: { element: ".new-project-btn", on: "bottom" },
  buttons: [{ text: "Next", action: tour.next }],
});

tour.start();
Shepherd's imperative style: explicit steps, explicit buttons, full control.

Driver.jsthe lightweight one

License:
MIT
Best for:
Simple highlight-and-explain tours with minimal footprint

Driver.js is the smallest and most pleasant of the four for basic work: highlight an element, show a popover, move on. The v1 rewrite made it dependency-free and a few kilobytes, and the API fits in your head after one reading.

It is deliberately less featureful — fewer step behaviors, less lifecycle control — which is either its weakness or exactly why you'd pick it. For a two-step "here's the new thing" highlight, reaching for anything heavier is ceremony.

import { driver } from "driver.js";
import "driver.js/dist/driver.css";

const tour = driver({
  showProgress: true,
  steps: [
    {
      element: ".new-project-btn",
      popover: { title: "Start here", description: "Create your first project." },
    },
    {
      element: ".invite-field",
      popover: { title: "Bring the team", description: "Invite a teammate early." },
    },
  ],
});

tour.drive();
Driver.js v1: the whole API is roughly this.

Intro.jsthe famous one, with a licensing trap

License:
AGPL-3.0, or a paid commercial license
Best for:
Open-source projects, or teams that buy the commercial license

Intro.js is the name everyone knows and the demo everyone has seen — mature, polished, with hints and tooltips beyond basic tours. Then the fine print: it is AGPL-3.0 licensed, and for a commercial SaaS product the AGPL's obligations are ones almost no company will accept, which makes the paid commercial license the real price of using it.

Nothing wrong with paying for software — but teams routinely npm-install Intro.js without reading the license and discover the issue in a legal review months later. If you use it commercially, buy the license; if that changes the math, the MIT options above exist.

import introJs from "intro.js";
import "intro.js/introjs.css";

introJs()
  .setOptions({
    steps: [
      { element: document.querySelector(".new-project-btn"), intro: "Everything starts here." },
      { element: document.querySelector(".invite-field"), intro: "Invite a teammate early." },
    ],
  })
  .start();
Intro.js quick start — read the license before this ships to production.
the honest accounting

What every library leaves you to build.

The tour is the visible fifth of an onboarding system. These four builds are the rest — and they are where hand-rolled setups quietly stall.

Who edits the copy?

With every library above, changing a step's wording is a pull request and a deploy. The growth PM who owns activation files a ticket and waits — which is the exact dependency onboarding tooling exists to remove.

Targeting is a second project

Show the tour to new users only, skip it for invited teammates, re-show it to users who never finished — every library leaves audiences, traits, and rules entirely to you. This is routinely more code than the tour itself.

Seen-state is a third one

"Has this user seen this tour?" needs storage, per user, ideally across devices. localStorage gets you a demo; production needs it in your backend, with an API, forever.

Analytics is a fourth

Which step do users abandon? Did the tour move activation? Libraries emit events at best — the funnel, the dashboards, and the A/B harness to answer the only questions that matter are all separate builds.

build vs. buy

The whole trade, in one table.

DimensionBuild on a libraryBuy (Wakeline)
Upfront costFree (library) + engineering days to integrateFree tier, then $49/mo for 25,000 MAU
Who edits a step's copyAn engineer, via a pull request and a deployAnyone on the team, in a visual builder, live in seconds
Targeting & segmentationYou build it — user traits, rules, audiencesIncluded: audiences, trait rules, saved segments
AnalyticsYou build it — step events, funnels, dashboardsIncluded: per-step funnels, trends, click actions
Seen-state & persistenceYou build it — per user, across devicesIncluded, cross-device for identified users
Surfaces beyond toursChecklists, surveys, banners: all separate buildsIncluded in the same builder
A/B testingYou build it, including the statisticsIncluded, with significance testing
Ongoing maintenanceYours — selector breakage, library upgrades, edge casesVendor's — anchors degrade gracefully, tooling maintained
the buy side

Wakeline with React: deliberately boring.

Wakeline sits on the buy side of that table, and the React story is deliberately boring: there is no React SDK, no wrapper components, no build-step integration. The snippet runs at the DOM level, which is the level React renders to — so it works with every React version, router, and meta-framework without knowing or caring which one you use.

  • Anchors survive re-renders. Steps attach by selector to the rendered DOM; React re-rendering an element doesn't detach them, and steps wait for lazily-rendered elements (or skip gracefully) instead of pointing at nothing.
  • SSR and Next.js are non-events. The SDK is client-side and loads async — server components, hydration, and streaming don't interact with it.
  • Tours are built visually on your live app, no-code — the PM edits copy at 4pm and it's live at 4:01, with no ticket in sight.
  • The rest of the system is included: targeting and segments, cross-device seen-state, per-step funnels, A/B testing, plus checklists, surveys, and banners from the same builder.
<script>
  (function (w, d, s) {
    w.Wakeline = w.Wakeline || function () { (w.Wakeline.q = w.Wakeline.q || []).push(arguments); };
    var js = d.createElement("script"); js.async = 1; js.src = s;
    var f = d.getElementsByTagName("script")[0]; f.parentNode.insertBefore(js, f);
  })(window, document, "https://wakeline.io/wakeline.js");

  Wakeline("init", { key: "YOUR_PUBLISHABLE_KEY" });
  // After login — unlocks targeting, segments, and cross-device state:
  Wakeline("identify", user.id, { name: user.name, plan: user.plan });
</script>
The entire integration, for any React app. There is no step two.
honest limits

When building on a library is still right.

One static tour, engineer-owned

A single walkthrough in a developer tool, written once and rarely touched, is a fine fit for Joyride or Driver.js — the system gaps above never bite because there's no program, just a tour.

Your users are developers

If your product ships to engineers who'd notice and respect a hand-rolled tour, the polish calculus shifts — and your team can genuinely maintain it.

Zero budget, genuinely

Pre-revenue and counting dollars? MIT libraries are free forever. (Though a free tier covering 1,000 MAU exists precisely for this stage — compare the engineering hours honestly.)

Total control is a requirement

If the tour must do something no vendor's renderer does — bespoke animation, deep product coupling — you were always going to build. Build on Shepherd; it will fight you least.

questions

React onboarding tours: common questions.

Still stuck? Read the docs or talk to us.

What is the best product tour library for React?

React Joyride for the most React-idiomatic developer experience, Driver.js for minimal-footprint highlighting, Shepherd.js for maximum control. All three are MIT. Intro.js is equally capable but AGPL-licensed — commercial use realistically requires buying its paid license.

Is Intro.js free for commercial use?

Effectively no. Intro.js is AGPL-3.0, whose obligations are unacceptable to almost every commercial product, so its paid commercial license is the practical price of shipping it in a SaaS app. Teams regularly discover this in legal review after integrating — check licenses before you npm install.

Does Wakeline have a React SDK?

Deliberately not — there is nothing framework-specific to install. The snippet works at the DOM level React renders to, so every React version, Next.js, Remix, and plain Vite apps all integrate identically: paste the snippet, call identify() after login, build tours visually.

How do tours handle React re-renders and route changes?

Wakeline anchors steps by selector to the live DOM and waits for elements to appear (or skips gracefully), so re-renders, client-side route changes, and lazy-loaded views behave. The same property is what makes tours survive your app's ordinary churn without an engineer re-wiring them.

Library or platform — how do I actually decide?

Count the tours and the editors. One tour, edited by engineers: use a library. A program — multiple flows, targeting, measurement, non-engineers iterating: the build-vs-buy table above is the honest accounting, and the four system gaps (editing, targeting, persistence, analytics) are where hand-rolled setups quietly die.

Ship a React tour today — without the build.

The snippet takes five minutes; the free plan covers 1,000 monthly active users. Compare it against the library route on your own app.