Skip to content

Repository files navigation

GlowTour.js - Show people around, without writing a guide.

GlowTour.js

Guided product tours, onboarding walkthroughs and feature spotlights - one engine, five frameworks.

A guided-tour library for React, Vue, Solid, Angular and vanilla JavaScript.
Accessible by default, SSR-verified, zero runtime dependencies, ESM-only, written in TypeScript.

npm version CI bundle size MIT license types included ESM only

Website · Documentation · Live examples · Issues

A GlowTour.js walkthrough highlighting controls in an analytics dashboard


Why GlowTour.js

Most tour libraries are a single DOM script with framework wrappers bolted on, or a framework component you cannot reuse anywhere else. GlowTour.js splits the two: a framework-agnostic engine that owns workflow state, positioning and DOM behavior, and native adapters - real React components, real Vue components, real Angular standalone components - that render it.

Five native adapters React, Vue 3, Solid, Angular 18, and native custom elements - not one wrapper reskinned five times.
Accessible by default role="dialog", aria-live step description, focus trap, focus restoration, Escape and arrow-key shortcuts, everywhere.
SSR-verified renderToString + hydration coverage, plus real Next.js, Nuxt, SolidStart and Angular apps tested with Playwright.
Zero runtime dependencies The core ships no dependencies and touches no browser global you did not hand it, which is what keeps SSR safe.
Themeable from CSS alone Every color, radius, spacing, size and transition is a --glow-tour-* token; the popover inherits your font.
Analytics-ready One onEvent callback reports every start, step and exit - with the step id, its duration, and how the user left.
Steps that wait .do(), .wait(), .waitUntil(), .waitUntilElement() and .onTargetEvent() sequence real work between steps.

Looking for an alternative to Driver.js, Intro.js, Shepherd.js or React Joyride that keeps first-class TypeScript types, server rendering and accessibility across more than one framework? That is the gap this fills.

Install

# pick the adapter for your framework, plus the default theme
npm i @glowhop/react-tour @glowhop/styles-tour
Other frameworks
npm i @glowhop/vue-tour      @glowhop/styles-tour   # Vue 3
npm i @glowhop/solid-tour    @glowhop/styles-tour   # Solid
npm i @glowhop/angular-tour  @glowhop/styles-tour   # Angular 18
npm i @glowhop/vanilla-tour  @glowhop/styles-tour   # custom elements

The adapters depend on @glowhop/core-tour; you only install it directly to build your own adapter.

Quick start

import "@glowhop/styles-tour/default.css";
import { createGlowTour, DefaultTour } from "@glowhop/react-tour";

const tour = createGlowTour();
const workflow = tour
  .create("welcome")
  .step({ id: "name", target: "#workspace-name", title: "Name it", content: "Anything you like." })
  .step({ id: "save", target: "#save", title: "Save", content: "That's the whole tour." })
  .build();

export function App() {
  return (
    <>
      <input id="workspace-name" />
      <button id="save">Save</button>
      <button type="button" onClick={() => void tour.run(workflow)}>Start tour</button>
      <DefaultTour tour={tour} />
    </>
  );
}

Import the stylesheet once. Nothing renders until an adapter connects a root and a popover - the core owns workflow state, navigation and DOM behavior, but no presentation.

The same workflow in Vue, Solid, Angular and vanilla JS.

Theming

The default theme declares every token at zero specificity, so a plain class on any ancestor of the tour re-skins it - no component has to be re-composed:

.terminal-tour {
  font-family: ui-monospace, Menlo, monospace; /* the popover is `font: inherit` */

  --glow-tour-color-accent: #35f0a0;
  --glow-tour-color-surface: #071a12;
  --glow-tour-color-text: #d6ffe9;
  --glow-tour-radius: 2px;
  --glow-tour-popover-width: 320px;
}

The same GlowTour.js popover re-skinned from CSS custom properties

Light and dark ship together; data-glow-tour-theme="dark" on a wrapper pins one. See the Custom theme example.

Placement

popover.placementTryOrder walks top / bottom / left / right until one fits, then falls back to centering - the same collision logic drives the pointer.

Packages

Package What it is
@glowhop/react-tour React 18 / 19 adapter
@glowhop/vue-tour Vue 3 adapter
@glowhop/solid-tour Solid adapter
@glowhop/angular-tour Angular 18 adapter
@glowhop/vanilla-tour Native custom elements, no framework
@glowhop/styles-tour Default theme, light and dark
@glowhop/core-tour Workflow controller and DOM driver; never used standalone

Writing an adapter for another framework? @glowhop/core-tour/adapter is the entry point.

Documentation

Contributing

bun install --frozen-lockfile
bun run check && bun run typecheck && bun test && bun run build && bun run pack

bun run docs serves the website, bun run playground the scratch app. Changesets versions the public packages together; a published GitHub Release triggers the OIDC npm workflow (release notes).

Issues and pull requests are welcome - bug reports are most useful with the framework, adapter version, and a minimal reproduction.

License

MIT © Glowhop

About

Guided product tours and onboarding walkthroughs for React, Vue, Solid, Angular and vanilla JS. Accessible, SSR-verified, zero dependencies.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

19 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages