npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nextsparkjs/plugin-walkme

v0.1.0-beta.113

Published

Guided tours and onboarding system for NextSpark applications. Supports declarative tour definitions, multiple step types (tooltip, modal, spotlight, beacon), cross-page navigation, conditional triggers, localStorage persistence, full keyboard accessibili

Downloads

852

Readme

@nextsparkjs/plugin-walkme

Guided tours and onboarding system for NextSpark applications. Supports declarative tour definitions, multiple step types (tooltip, modal, spotlight, beacon), cross-page navigation, conditional triggers, localStorage persistence, full keyboard accessibility, and i18n.

Installation

The plugin is available as an npm package or can be copied directly into your project.

npm (recommended)

pnpm add @nextsparkjs/plugin-walkme

Manual

Copy the plugins/walkme/ directory to contents/plugins/walkme/ in your NextSpark project.

Register the plugin

Add 'walkme' to your theme configuration:

// contents/themes/<your-theme>/config/theme.config.ts
export const themeConfig: ThemeConfig = {
  plugins: ['walkme'],
}

Rebuild the registry:

node core/scripts/build/registry.mjs

Quick Start

1. Define a tour

import type { Tour } from '@nextsparkjs/plugin-walkme/types/walkme.types'

const onboardingTour: Tour = {
  id: 'getting-started',
  name: 'Getting Started',
  trigger: { type: 'onFirstVisit', delay: 1000 },
  steps: [
    {
      id: 'welcome',
      type: 'modal',
      title: 'Welcome!',
      content: 'Let us show you around the application.',
      actions: ['next', 'skip'],
    },
    {
      id: 'sidebar',
      type: 'tooltip',
      target: '[data-cy="sidebar-nav"]',
      title: 'Navigation',
      content: 'Use the sidebar to navigate between sections.',
      position: 'right',
      actions: ['next', 'prev', 'skip'],
    },
    {
      id: 'create',
      type: 'spotlight',
      target: '[data-cy="create-button"]',
      title: 'Create New Item',
      content: 'Click here to create your first item.',
      actions: ['complete', 'prev'],
    },
  ],
}

2. Wrap your app with the provider

import { WalkmeProvider } from '@nextsparkjs/plugin-walkme/components/WalkmeProvider'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <WalkmeProvider tours={[onboardingTour]} autoStart>
      {children}
    </WalkmeProvider>
  )
}

3. (Optional) Control tours programmatically

import { useWalkme } from '@nextsparkjs/plugin-walkme/hooks/useWalkme'

function HelpButton() {
  const { startTour, isActive } = useWalkme()

  return (
    <button onClick={() => startTour('getting-started')} disabled={isActive}>
      Start Tour
    </button>
  )
}

Configuration

Tour

| Property | Type | Required | Description | |---|---|---|---| | id | string | Yes | Unique identifier | | name | string | Yes | Human-readable name | | description | string | No | Tour description | | trigger | TourTrigger | Yes | When and how the tour activates | | conditions | TourConditions | No | Conditions that must be met to show the tour | | steps | TourStep[] | Yes | Ordered list of steps (minimum 1) | | onComplete | () => void | No | Callback on tour completion | | onSkip | () => void | No | Callback on tour skip | | priority | number | No | Auto-trigger ordering (lower = higher priority) |

TourStep

| Property | Type | Required | Description | |---|---|---|---| | id | string | Yes | Unique step identifier | | type | StepType | Yes | 'tooltip' | 'modal' | 'spotlight' | 'beacon' | 'floating' | | title | string | Yes | Step title | | content | string | Yes | Step body text | | target | string | Conditional | CSS selector or data-walkme-target value. Required for tooltip, spotlight, beacon | | route | string | No | Route path for cross-page steps | | position | StepPosition | No | 'top' | 'bottom' | 'left' | 'right' | 'auto'. Default: 'auto' | | actions | StepAction[] | Yes | Available actions: 'next', 'prev', 'skip', 'complete', 'close' | | delay | number | No | Delay in ms before showing | | autoAdvance | number | No | Auto-advance after this many ms | | beforeShow | () => void | No | Callback before the step renders | | afterShow | () => void | No | Callback after the step renders |

Step Types

  • modal - Centered overlay modal. No target element required. Use for welcome screens and informational messages.
  • tooltip - Anchored tooltip positioned next to a target element. Requires target.
  • spotlight - Overlay with a cutout around the target element plus a tooltip. Requires target.
  • beacon - Pulsing indicator on a target element that expands on click. Requires target.
  • floating - Same as modal. Alias for centered content without a target.

TourTrigger

| Property | Type | Required | Description | |---|---|---|---| | type | TriggerType | Yes | 'onFirstVisit' | 'onRouteEnter' | 'onEvent' | 'manual' | 'scheduled' | | delay | number | No | Delay in ms before activating | | route | string | No | Route pattern for onRouteEnter (supports * and ** wildcards) | | event | string | No | Event name for onEvent trigger | | afterVisits | number | No | Activate after N visits (for scheduled) | | afterDays | number | No | Activate after N days since first visit (for scheduled) |

Trigger types:

  • onFirstVisit - Fires on the user's first page visit (visitCount === 1).
  • onRouteEnter - Fires when the user navigates to a matching route. Supports exact matches (/dashboard), wildcard (/admin/*), and glob (/docs/**).
  • onEvent - Fires when a custom event is emitted via emitEvent().
  • manual - Never auto-triggers. Start programmatically with startTour(tourId).
  • scheduled - Fires after a number of visits (afterVisits) or days since first visit (afterDays).

TourConditions

All conditions use AND logic. Every specified condition must pass.

| Property | Type | Description | |---|---|---| | userRole | string[] | User must have one of these roles | | featureFlags | string[] | All specified flags must be active | | completedTours | string[] | All specified tours must be completed first | | notCompletedTours | string[] | None of these tours should be completed | | custom | (ctx: ConditionContext) => boolean | Custom condition function |

WalkmeProvider Props

| Prop | Type | Default | Description | |---|---|---|---| | tours | Tour[] | Required | Array of tour definitions (validated with Zod at runtime) | | children | ReactNode | Required | Application content | | debug | boolean | false | Enable debug logging to console | | autoStart | boolean | true | Auto-start eligible tours based on triggers | | autoStartDelay | number | 1000 | Default delay before auto-starting tours (ms) | | persistState | boolean | true | Persist tour state in localStorage | | onTourStart | (event: TourEvent) => void | - | Callback when a tour starts | | onTourComplete | (event: TourEvent) => void | - | Callback when a tour completes | | onTourSkip | (event: TourEvent) => void | - | Callback when a tour is skipped | | onStepChange | (event: TourEvent) => void | - | Callback when the active step changes | | conditionContext | Partial<ConditionContext> | - | External context for condition evaluation (userRole, featureFlags) |

Hooks

useWalkme()

Main hook for controlling tours. Must be used within a <WalkmeProvider>.

const {
  // Tour control
  startTour,      // (tourId: string) => void
  pauseTour,      // () => void
  resumeTour,     // () => void
  skipTour,       // () => void
  completeTour,   // () => void
  resetTour,      // (tourId: string) => void
  resetAllTours,  // () => void

  // Step navigation
  nextStep,       // () => void
  prevStep,       // () => void
  goToStep,       // (stepIndex: number) => void

  // State
  isActive,       // boolean - whether any tour is active
  activeTourId,   // string | null
  currentStepIndex, // number

  // Queries
  getActiveTour,  // () => Tour | null
  getActiveStep,  // () => TourStep | null
  isTourCompleted, // (tourId: string) => boolean
  isTourActive,   // (tourId: string) => boolean

  // Events
  emitEvent,      // (eventName: string) => void
} = useWalkme()

useTour(tourId)

Hook for tracking the state of a specific tour.

const {
  tour,           // Tour | null - full tour definition
  isActive,       // boolean
  isCompleted,    // boolean
  isSkipped,      // boolean
  currentStep,    // number (-1 if not active)
  totalSteps,     // number
  progress,       // number (0-100)
  start,          // () => void - start this tour
  reset,          // () => void - reset this tour
} = useTour('getting-started')

useTourProgress()

Hook for tracking global completion progress across all tours.

const {
  completedTours,   // number
  totalTours,       // number
  percentage,       // number (0-100)
  completedTourIds, // string[]
  skippedTourIds,   // string[]
  remainingTours,   // number
} = useTourProgress()

Examples

Single-Page Tour

A basic onboarding flow on one page:

const basicTour: Tour = {
  id: 'getting-started',
  name: 'Getting Started',
  trigger: { type: 'onFirstVisit', delay: 1000 },
  steps: [
    {
      id: 'welcome',
      type: 'modal',
      title: 'Welcome!',
      content: 'Let us show you around.',
      actions: ['next', 'skip'],
    },
    {
      id: 'sidebar',
      type: 'tooltip',
      target: '[data-cy="sidebar-nav"]',
      title: 'Navigation',
      content: 'Use the sidebar to navigate.',
      position: 'right',
      actions: ['next', 'prev', 'skip'],
    },
    {
      id: 'create',
      type: 'spotlight',
      target: '[data-cy="create-button"]',
      title: 'Create Item',
      content: 'Click here to create your first item.',
      actions: ['complete', 'prev'],
    },
  ],
}

Cross-Page Tour

Navigate users between pages during a tour:

const crossPageTour: Tour = {
  id: 'explore-app',
  name: 'Explore the App',
  trigger: { type: 'manual' },
  conditions: { completedTours: ['getting-started'] },
  steps: [
    {
      id: 'dashboard',
      type: 'tooltip',
      target: '[data-cy="dashboard-stats"]',
      title: 'Your Stats',
      content: 'Key metrics at a glance.',
      position: 'bottom',
      route: '/dashboard',
      actions: ['next', 'skip'],
    },
    {
      id: 'profile',
      type: 'spotlight',
      target: '[data-cy="profile-form"]',
      title: 'Your Profile',
      content: 'Complete your profile.',
      route: '/settings/profile',
      actions: ['complete', 'prev'],
    },
  ],
}

When the tour reaches a step with a route that differs from the current page, the provider automatically navigates using router.push() and waits for the target element to appear.

Conditional Tour

Show tours only to specific user roles or when feature flags are active:

const adminTour: Tour = {
  id: 'admin-features',
  name: 'Admin Features',
  priority: 10,
  trigger: { type: 'onRouteEnter', route: '/admin/*', delay: 500 },
  conditions: {
    userRole: ['admin', 'superadmin'],
    completedTours: ['getting-started'],
    featureFlags: ['admin-panel-v2'],
  },
  steps: [
    {
      id: 'admin-welcome',
      type: 'modal',
      title: 'Admin Dashboard',
      content: 'Here are the key admin features.',
      actions: ['next', 'skip'],
    },
    // ...more steps
  ],
}

Pass the user context to the provider:

<WalkmeProvider
  tours={[adminTour]}
  conditionContext={{
    userRole: currentUser.role,
    featureFlags: activeFlags,
  }}
>
  <App />
</WalkmeProvider>

Programmatic Tour Control

Start tours on demand and track completion:

function OnboardingDashboard() {
  const { startTour } = useWalkme()
  const { completedTours, totalTours, percentage } = useTourProgress()
  const intro = useTour('getting-started')

  return (
    <div>
      <h2>Onboarding Progress: {percentage}%</h2>
      <p>{completedTours} of {totalTours} tours completed</p>

      {!intro.isCompleted && (
        <button onClick={intro.start}>Start Getting Started Tour</button>
      )}

      <button onClick={() => startTour('advanced-features')}>
        Show Advanced Features
      </button>
    </div>
  )
}

Analytics Integration

Track tour events for analytics:

<WalkmeProvider
  tours={tours}
  onTourStart={(event) => {
    analytics.track('tour_started', {
      tourId: event.tourId,
      tourName: event.tourName,
    })
  }}
  onTourComplete={(event) => {
    analytics.track('tour_completed', {
      tourId: event.tourId,
      totalSteps: event.totalSteps,
    })
  }}
  onTourSkip={(event) => {
    analytics.track('tour_skipped', {
      tourId: event.tourId,
      stepIndex: event.stepIndex,
    })
  }}
  onStepChange={(event) => {
    analytics.track('step_changed', {
      tourId: event.tourId,
      stepId: event.stepId,
      stepIndex: event.stepIndex,
    })
  }}
>

Customization

Element Targeting

Steps can target elements using:

  1. CSS selectors - #my-id, .my-class, [data-cy="value"]
  2. data-walkme-target - Add data-walkme-target="name" to any element, then reference as target: "name" (plain string without special CSS chars)
  3. data-cy - Standard test selectors: target: '[data-cy="create-button"]'

If the target element is not found within 5 seconds, the step renders without an anchor and a warning is logged in debug mode.

Internationalization

The plugin ships with English and Spanish translations under plugins/walkme/messages/. To add more languages, create a JSON file following the same structure:

{
  "walkme": {
    "next": "Next",
    "prev": "Previous",
    "skip": "Skip tour",
    "complete": "Complete",
    "close": "Close",
    "progress": "Step {current} of {total}",
    "tourAvailable": "Tour available",
    "beaconLabel": "Click to start guided tour",
    "modalTitle": "Guided Tour",
    "tooltipLabel": "Tour step",
    "spotlightLabel": "Highlighted element",
    "keyboardHint": "Press Arrow Right for next step, Escape to skip",
    "tourCompleted": "Tour completed!",
    "tourSkipped": "Tour skipped",
    "errorTargetNotFound": "Element not found, skipping step",
    "resumeTour": "Resume tour",
    "restartTour": "Restart tour"
  }
}

CSS Variables (Theming)

All walkme components use CSS custom properties for styling. Override them in your global CSS or scoped styles:

:root {
  --walkme-bg: #ffffff;
  --walkme-text: #111827;
  --walkme-text-muted: #6b7280;
  --walkme-primary: #3b82f6;
  --walkme-border: #e5e7eb;
  --walkme-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  --walkme-beacon-color: #3b82f6;
  --walkme-overlay-color: rgba(0, 0, 0, 0.5);
}

/* Dark mode */
.dark {
  --walkme-bg: #1f2937;
  --walkme-text: #f9fafb;
  --walkme-text-muted: #9ca3af;
  --walkme-primary: #60a5fa;
  --walkme-border: #374151;
  --walkme-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
  --walkme-beacon-color: #60a5fa;
  --walkme-overlay-color: rgba(0, 0, 0, 0.7);
}

Keyboard Navigation

When a tour is active:

| Key | Action | |---|---| | Arrow Right | Next step | | Arrow Left | Previous step | | Escape | Skip tour | | Tab | Cycle focus within modals (focus trap) |

Accessibility

  • All interactive elements have ARIA labels and roles
  • Focus is trapped within modals
  • Focus is restored to the previously focused element when a tour ends
  • Progress bar uses role="progressbar" with aria-valuenow/aria-valuemin/aria-valuemax
  • Keyboard navigation is fully supported

Environment Variables

| Variable | Default | Description | |---|---|---| | WALKME_ENABLED | true | Enable/disable the plugin | | WALKME_DEBUG | false | Debug mode | | WALKME_AUTO_START | true | Auto-start tours on first visit | | WALKME_AUTO_START_DELAY | 1000 | Delay before auto-starting (ms) | | WALKME_PERSIST_STATE | true | Persist state in localStorage | | WALKME_ANALYTICS_ENABLED | false | Enable analytics event emission |

Troubleshooting

Tour does not start automatically

  1. Check that autoStart is true on the provider (default).
  2. Verify the trigger type matches the situation (e.g., onFirstVisit only fires when visitCount === 1).
  3. Check that all conditions are met (role, feature flags, completed tours).
  4. Open debug mode (debug={true}) to see console logs.
  5. If using persistState, the tour may already be marked as completed or skipped in localStorage. Call resetTour(tourId) or clear walkme-state from localStorage.

Target element not found

  1. Ensure the target selector is correct and the element exists in the DOM.
  2. For dynamically rendered elements, the plugin waits up to 5 seconds using MutationObserver + polling.
  3. Use data-walkme-target="name" for elements that are hard to select with CSS.
  4. Enable debug mode to see warnings about missing targets.

Cross-page navigation not working

  1. Verify the route property on each step matches the actual path.
  2. The provider uses router.push() from next/navigation. Ensure you are using the App Router.
  3. If navigation fails, the step is automatically skipped and the tour advances.

State not persisting

  1. Check that persistState is true (default).
  2. Verify localStorage is available (not in private browsing mode or incognito with storage disabled).
  3. The state is stored under the key walkme-state in localStorage.

File Structure

plugins/walkme/
  plugin.config.ts          # Plugin configuration
  package.json              # Dependencies and metadata
  .env.example              # Environment variable template
  types/
    walkme.types.ts         # All TypeScript type definitions
  lib/
    core.ts                 # Pure-function state machine (reducer + helpers)
    validation.ts           # Zod schemas for tour config validation
    storage.ts              # localStorage persistence adapter
    targeting.ts            # DOM element targeting (CSS, data-walkme, data-cy)
    positioning.ts          # @floating-ui/react wrapper for smart positioning
    triggers.ts             # Tour trigger evaluation
    conditions.ts           # Tour condition evaluation (AND logic)
    plugin-env.ts           # Environment variable loader
  hooks/
    useWalkme.ts            # Main public hook (tour control + navigation)
    useTour.ts              # Per-tour state hook
    useTourProgress.ts      # Global completion progress hook
    useTourState.ts         # Internal: state machine + localStorage sync
  providers/
    walkme-context.ts       # React Context definition
  components/
    WalkmeProvider.tsx      # Main provider (state, triggers, rendering)
    WalkmeOverlay.tsx       # Semi-transparent backdrop overlay
    WalkmeTooltip.tsx       # Positioned tooltip step
    WalkmeModal.tsx         # Centered modal step with focus trap
    WalkmeSpotlight.tsx     # Overlay with cutout + tooltip
    WalkmeBeacon.tsx        # Pulsing indicator
    WalkmeProgress.tsx      # Progress bar
    WalkmeControls.tsx      # Navigation buttons (Next/Prev/Skip/Complete)
  messages/
    en.json                 # English translations
    es.json                 # Spanish translations
  examples/
    basic-tour.ts           # Single-page tour example
    cross-window-tour.ts    # Multi-page tour example
    conditional-tour.ts     # Role-based conditional tour example
  tests/
    setup.ts                # Jest test setup
    tsconfig.json           # Test-specific TypeScript config
    lib/                    # Unit tests for all lib modules (165 tests)
  jest.config.cjs           # Jest configuration

Dependencies

  • @floating-ui/react ^0.27.0 - Smart positioning for tooltips and popovers

Peer dependencies (provided by the host project): react, react-dom, next, zod, next-intl, lucide-react, class-variance-authority, clsx, tailwind-merge.