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

web-tourify

v2.0.0

Published

A lightweight product tour library for Next.js with RTL support, auto-mirroring, and font customization

Readme

Web-Tourify

A lightweight, customizable product tour library for Next.js applications with full RTL (right-to-left) support.

npm version license


Table of Contents


Features

  • RTL Support - Full right-to-left language support with automatic tooltip position mirroring
  • Auto-Mirroring - Tooltip positions automatically flip for RTL layouts (left ↔ right)
  • Custom Fonts - Configurable fonts for LTR and RTL with automatic Google Fonts loading
  • Multiple Tours - Create and manage multiple product tours in a single application
  • Route Navigation - Steps can automatically navigate between routes using Next.js router
  • Fully Customizable - Custom card components, animations, transitions, and styling
  • 12 Tooltip Positions - Flexible positioning with top, bottom, left, right, and corner variants
  • Smooth Animations - Powered by Framer Motion for fluid transitions
  • TypeScript Ready - Full TypeScript support with exported types
  • Lightweight - Minimal bundle size with peer dependencies

Installation

# npm
npm install web-tourify

# pnpm
pnpm add web-tourify

# yarn
yarn add web-tourify

Peer Dependencies

Make sure you have the following peer dependencies installed:

npm install react react-dom next framer-motion @radix-ui/react-portal

| Package | Version | |---------|---------| | react | >= 18 | | react-dom | >= 18 | | next | >= 13 | | framer-motion | >= 11 | | @radix-ui/react-portal | >= 1.1.1 |


Quick Start

1. Setup Provider and Component

Wrap your application with WebTourifyProvider and WebTourify in your root layout:

// app/layout.tsx
import { WebTourifyProvider, WebTourify } from "web-tourify";
import { CustomCard } from "@/components/CustomCard";
import { tourSteps } from "@/lib/tour-steps";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <WebTourifyProvider>
          <WebTourify
            steps={tourSteps}
            cardComponent={CustomCard}
          >
            {children}
          </WebTourify>
        </WebTourifyProvider>
      </body>
    </html>
  );
}

2. Define Your Tour Steps

// lib/tour-steps.tsx
import { Tour } from "web-tourify";

export const tourSteps: Tour[] = [
  {
    tour: "welcome-tour",
    steps: [
      {
        icon: "👋",
        title: "Welcome!",
        content: "Let's take a quick tour of the application.",
        selector: "#welcome-element",
        side: "bottom",
        pointerPadding: 10,
        pointerRadius: 10,
      },
      {
        icon: "📝",
        title: "Create Content",
        content: "Click here to create your first item.",
        selector: "#create-button",
        side: "right",
      },
      {
        icon: "⚙️",
        title: "Settings",
        content: "Customize your experience in settings.",
        selector: "#settings-menu",
        side: "left",
        nextRoute: "/settings",
      },
    ],
  },
];

3. Add Target Elements

Add id attributes to elements you want to highlight:

// app/page.tsx
export default function Home() {
  return (
    <div>
      <h1 id="welcome-element">Welcome to Our App</h1>
      <button id="create-button">Create New</button>
      <nav id="settings-menu">Settings</nav>
    </div>
  );
}

4. Start the Tour

"use client";
import { useWebTourify } from "web-tourify";

export function StartTourButton() {
  const { startTour } = useWebTourify();

  return (
    <button onClick={() => startTour("welcome-tour")}>
      Start Tour
    </button>
  );
}

RTL Support

Web-Tourify provides comprehensive RTL (right-to-left) support for internationalized applications.

Global Direction

Set a default direction for all tours:

<WebTourify
  steps={tourSteps}
  direction="rtl"
  cardComponent={CustomCard}
>
  {children}
</WebTourify>

Per-Tour Direction

Override direction for specific tours:

const tourSteps: Tour[] = [
  {
    tour: "english-tour",
    direction: "ltr",
    steps: [/* ... */],
  },
  {
    tour: "arabic-tour",
    direction: "rtl",
    steps: [/* ... */],
  },
];

Auto-Mirroring

When RTL is active, horizontal tooltip positions are automatically mirrored:

| Original | Mirrored (RTL) | |----------|----------------| | left | right | | right | left | | top-left | top-right | | top-right | top-left | | bottom-left | bottom-right | | bottom-right | bottom-left | | left-top | right-top | | left-bottom | right-bottom | | right-top | left-top | | right-bottom | left-bottom |

Disable auto-mirroring if you want manual control:

<WebTourify autoMirror={false} /* ... */ />

Font Configuration

Default fonts are automatically loaded from Google Fonts:

  • LTR: DM Sans
  • RTL: Noto Kufi Arabic

Customize fonts:

<WebTourify
  fontConfig={{
    ltr: "'Inter', sans-serif",
    rtl: "'Cairo', sans-serif",
    loadFonts: false, // Disable automatic Google Fonts injection
  }}
  /* ... */
/>

Custom Card Component

Create a fully custom tooltip component to match your design system:

"use client";
import type { CardComponentProps } from "web-tourify";

export function CustomCard({
  step,
  currentStep,
  totalSteps,
  nextStep,
  prevStep,
  arrow,
  direction,
}: CardComponentProps) {
  return (
    <div
      className="bg-white rounded-lg shadow-xl p-6 max-w-sm"
      style={{ direction }}
    >
      {/* Arrow indicator */}
      <div className="text-white">{arrow}</div>

      {/* Header */}
      <div className="flex items-center gap-2 mb-3">
        {step.icon && <span className="text-2xl">{step.icon}</span>}
        <h3 className="text-lg font-semibold">{step.title}</h3>
      </div>

      {/* Content */}
      <div className="text-gray-600 mb-4">{step.content}</div>

      {/* Progress */}
      <div className="text-sm text-gray-400 mb-4">
        Step {currentStep + 1} of {totalSteps}
      </div>

      {/* Navigation */}
      <div className="flex justify-between gap-2">
        <button
          onClick={prevStep}
          disabled={currentStep === 0}
          className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded disabled:opacity-50"
        >
          Previous
        </button>
        <button
          onClick={nextStep}
          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
        >
          {currentStep === totalSteps - 1 ? "Finish" : "Next"}
        </button>
      </div>
    </div>
  );
}

CardComponentProps

| Prop | Type | Description | |------|------|-------------| | step | Step | Current step configuration object | | currentStep | number | Zero-based index of current step | | totalSteps | number | Total number of steps in the tour | | nextStep | () => void | Function to advance to next step | | prevStep | () => void | Function to go to previous step | | arrow | JSX.Element | Pre-styled arrow SVG pointing to target | | direction | 'ltr' \| 'rtl' | Current text direction |


Tour Configuration

Tour Object

interface Tour {
  tour: string;           // Unique tour identifier
  steps: Step[];          // Array of step configurations
  direction?: Direction;  // Optional: 'ltr' | 'rtl'
}

Step Object

interface Step {
  // Content
  icon?: React.ReactNode | string | null;  // Icon or emoji
  title: string;                            // Step title
  content: React.ReactNode;                 // Step description/content
  selector: string;                         // CSS selector for target element

  // Positioning
  side?: Side;              // Tooltip position (default: 'bottom')
  pointerPadding?: number;  // Padding around highlighted element (default: 30)
  pointerRadius?: number;   // Border radius of highlight (default: 28)

  // Navigation
  nextRoute?: string;  // Route to navigate on "next"
  prevRoute?: string;  // Route to navigate on "previous"

  // Display
  showControls?: boolean;  // Show default controls (if using default card)
}

Side Positions

          top-left    top    top-right
                 ┌─────────┐
      left-top   │         │   right-top
                 │  TARGET │
   left-bottom   │         │   right-bottom
                 └─────────┘
       bottom-left  bottom  bottom-right

Available positions:

  • top, bottom, left, right
  • top-left, top-right, bottom-left, bottom-right
  • left-top, left-bottom, right-top, right-bottom

API Reference

WebTourifyProvider

Context provider that must wrap your application.

<WebTourifyProvider>
  {children}
</WebTourifyProvider>

WebTourify

Main component that renders the tour overlay.

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | React.ReactNode | required | Your application content | | steps | Tour[] | required | Array of tour configurations | | cardComponent | React.ComponentType<CardComponentProps> | - | Custom card component | | direction | 'ltr' \| 'rtl' | 'ltr' | Global text direction | | autoMirror | boolean | true | Auto-mirror positions for RTL | | fontConfig | FontConfig | - | Font configuration | | interact | boolean | false | Allow interaction with highlighted elements | | shadowRgb | string | '0, 0, 0' | RGB values for overlay shadow | | shadowOpacity | string | '0.2' | Opacity of overlay shadow | | cardTransition | Transition | { ease: 'anticipate', duration: 0.6 } | Framer Motion transition | | showTour | boolean | false | Control visibility externally |

FontConfig

interface FontConfig {
  ltr?: string;       // Font family for LTR (default: 'DM Sans')
  rtl?: string;       // Font family for RTL (default: 'Noto Kufi Arabic')
  loadFonts?: boolean; // Auto-load Google Fonts (default: true)
}

Programmatic Control

Use the useWebTourify hook to control tours from any component:

"use client";
import { useWebTourify } from "web-tourify";

export function TourControls() {
  const {
    startTour,
    closeTour,
    setCurrentStep,
    currentStep,
    currentTour,
    currentDirection,
    isVisible,
  } = useWebTourify();

  return (
    <div>
      {/* Start a specific tour */}
      <button onClick={() => startTour("welcome-tour")}>
        Start Welcome Tour
      </button>

      {/* Close the current tour */}
      <button onClick={() => closeTour()}>
        End Tour
      </button>

      {/* Jump to a specific step */}
      <button onClick={() => setCurrentStep(2)}>
        Go to Step 3
      </button>

      {/* With delay (useful after route changes) */}
      <button onClick={() => setCurrentStep(0, 500)}>
        Restart with delay
      </button>

      {/* Access current state */}
      <p>Current tour: {currentTour}</p>
      <p>Current step: {currentStep}</p>
      <p>Direction: {currentDirection}</p>
      <p>Visible: {isVisible ? 'Yes' : 'No'}</p>
    </div>
  );
}

Hook Return Values

| Property | Type | Description | |----------|------|-------------| | startTour | (tourName: string) => void | Start a tour by name | | closeTour | () => void | Close the current tour | | setCurrentStep | (step: number, delay?: number) => void | Jump to a specific step | | currentTour | string \| null | Name of active tour | | currentStep | number | Current step index | | currentDirection | 'ltr' \| 'rtl' | Current text direction | | isVisible | boolean | Whether tour is visible |


Styling

Tailwind CSS Configuration

If using the default styles, add web-tourify to your Tailwind content configuration:

// tailwind.config.js
module.exports = {
  content: [
    // ... your paths
    './node_modules/web-tourify/dist/**/*.{js,ts,jsx,tsx}',
  ],
  // ...
}

Custom Styling

The tour overlay uses these z-index values:

  • Overlay blocker: z-900
  • Pointer/highlight: z-900
  • Card: z-950

Target element during tour:

  • position: relative
  • z-index: 990 (when interact={true})

CSS Custom Properties

Style the arrow in your custom card:

[data-name="web-tourify-arrow"] {
  color: white; /* Arrow color inherits from currentColor */
}

Examples

Multi-language Tour

const tours: Tour[] = [
  {
    tour: "english-onboarding",
    direction: "ltr",
    steps: [
      {
        title: "Welcome",
        content: "Let's get started!",
        selector: "#hero",
        side: "bottom",
      },
    ],
  },
  {
    tour: "arabic-onboarding",
    direction: "rtl",
    steps: [
      {
        title: "مرحبا",
        content: "!هيا نبدأ",
        selector: "#hero",
        side: "bottom",
      },
    ],
  },
];

Tour with Route Navigation

const tours: Tour[] = [
  {
    tour: "full-app-tour",
    steps: [
      {
        title: "Dashboard",
        content: "This is your main dashboard.",
        selector: "#dashboard",
        side: "bottom",
        nextRoute: "/settings", // Navigate to settings on "next"
      },
      {
        title: "Settings",
        content: "Configure your preferences here.",
        selector: "#settings-panel",
        side: "left",
        prevRoute: "/", // Navigate back on "previous"
      },
    ],
  },
];

Conditional Tour Start

"use client";
import { useEffect } from "react";
import { useWebTourify } from "web-tourify";

export function OnboardingCheck() {
  const { startTour } = useWebTourify();

  useEffect(() => {
    const hasSeenTour = localStorage.getItem("hasSeenTour");
    if (!hasSeenTour) {
      startTour("welcome-tour");
      localStorage.setItem("hasSeenTour", "true");
    }
  }, [startTour]);

  return null;
}

TypeScript Support

All types are exported for TypeScript users:

import type {
  Tour,
  Step,
  Direction,
  FontConfig,
  CardComponentProps,
  WebTourifyProps,
  WebTourifyContextType,
} from "web-tourify";

License

MIT License - see LICENSE for details.


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


Support