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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@selvklart/onboarding

v2.0.0

Published

Lightweight onboarding library for Next.js

Readme

Selvklart Onboarding

Selvklart Onboarding is a lightweight, Next.js-focused onboarding library with smooth animations and smart positioning. Guide your users through interactive product tours with minimal setup.

Features

  • Smart Positioning - Automatic card positioning with viewport edge detection using Floating UI
  • Smooth Animations - Beautiful transitions powered by Motion (Framer Motion)
  • Celebration Effects - Built-in confetti animation on tour completion
  • Multi-Tour Support - Define and manage multiple product tours
  • Next.js Navigation - Seamless integration with Next.js App Router
  • Spotlight Effect - Customizable focus highlight on target elements
  • Internationalization - Full label customization for any language
  • Accessibility - ARIA labels and keyboard-ready structure
  • TypeScript First - Fully typed API with excellent IntelliSense support

Use Cases

  • Easier Onboarding - Guide new users with step-by-step tours
  • Engagement Boost - Make help docs interactive, so users learn by doing
  • Better Error Handling - Skip generic toasters—show users exactly what to fix with tailored tours
  • Event-Based Tours - Trigger custom tours after key actions to keep users coming back

Installation

npm install @selvklart/onboarding motion

Peer Dependencies

This library requires the following peer dependencies:

  • motion >= 11 (Framer Motion)
  • next >= 14
  • react >= 18
  • react-dom >= 18

Quick Start

'use client';

import {
  OnboardingProvider,
  Onboarding,
  useOnboarding,
  type Tour,
} from '@selvklart/onboarding';

// Define your tours
const tours: Tour[] = [
  {
    tour: 'welcome-tour',
    steps: [
      {
        icon: '👋',
        title: 'Welcome!',
        content: 'Let me show you around.',
        selector: '#welcome-button',
        side: 'bottom',
        showControls: true,
      },
      {
        icon: '✨',
        title: 'Amazing Feature',
        content: 'This is where the magic happens.',
        selector: '#feature-area',
        side: 'right',
        showControls: true,
      },
    ],
  },
];

// Component that uses the hook
function YourContent() {
  const { startOnboarding } = useOnboarding();

  return (
    <div>
      <button id="welcome-button" onClick={() => startOnboarding('welcome-tour')}>
        Start Tour
      </button>
      <div id="feature-area">Your feature content</div>
    </div>
  );
}

// Root layout or page
export default function App() {
  return (
    <OnboardingProvider>
      <Onboarding steps={tours}>
        <YourContent />
      </Onboarding>
    </OnboardingProvider>
  );
}

API Reference

<OnboardingProvider>

The context provider that manages onboarding state. Wrap your app with this component.

<OnboardingProvider>{children}</OnboardingProvider>

Props: None


<Onboarding>

The main component that renders the onboarding overlay, spotlight, and card.

<Onboarding
  steps={tours}
  shadowRgb="0, 0, 0"
  shadowOpacity="0.2"
  cardTransition={{ type: 'spring', bounce: 0.2 }}
  cardComponent={CustomCard}
  scrollToTop={true}
  interact={true}
  labels={customLabels}
>
  {children}
</Onboarding>

Props

| Prop | Type | Default | Description | | ---------------- | ----------------------------------- | -------------------------------------- | ------------------------------------------------------------ | | children | React.ReactNode | Required | Your application content | | steps | Tour[] | Required | Array of tour objects | | shadowRgb | string | '0, 0, 0' | RGB values for the spotlight shadow (e.g., '255, 0, 0') | | shadowOpacity | string | '0.2' | Opacity of the spotlight shadow (0-1) | | cardTransition | Transition | { type: 'spring', bounce: 0.2 } | Motion transition configuration | | cardComponent | React.ComponentType<CardProps> | DefaultCard | Custom card component | | scrollToTop | boolean | true | Scroll to top when tour ends | | interact | boolean | true | Allow interaction with highlighted element during tour | | labels | OnboardingLabels | English defaults (see Labels section) | Custom labels for internationalization |


useOnboarding() Hook

Access onboarding state and controls from anywhere in your app.

const {
  currentStep,
  currentTour,
  setCurrentStep,
  closeOnboarding,
  startOnboarding,
  isOnboardingVisible,
} = useOnboarding();

Return Values

| Property | Type | Description | | --------------------- | ----------------------------------------- | -------------------------------------------------- | | currentStep | number | Current step index (0-based) | | currentTour | string \| null | Current tour name | | setCurrentStep | (step: number, delay?: number) => void | Set the current step with optional delay (ms) | | closeOnboarding | () => void | Close/hide the onboarding overlay | | startOnboarding | (tourName: string) => void | Start a specific tour by name | | isOnboardingVisible | boolean | Whether the onboarding overlay is currently visible|


Tour Interface

Define multiple tours for different flows in your app.

interface Tour {
  tour: string;   // Unique tour identifier
  steps: Step[];  // Array of step objects
}

Example:

const tours: Tour[] = [
  {
    tour: 'first-visit',
    steps: [/* steps */],
  },
  {
    tour: 'advanced-features',
    steps: [/* steps */],
  },
];

Step Interface

Each step defines a single point in your onboarding tour.

interface Step {
  // Content
  icon?: React.ReactNode | string | null;
  title: string;
  content: React.ReactNode;
  selector: string;

  // Positioning
  side?: 'top' | 'bottom' | 'left' | 'right';
  pointerPadding?: number;
  pointerRadius?: number;

  // Navigation
  nextRoute?: string;
  prevRoute?: string;

  // UI
  showControls?: boolean;
}

Step Properties

| Property | Type | Default | Description | | ----------------- | ---------------------------------------- | ----------- | --------------------------------------------------------------------------- | | icon | React.ReactNode \| string \| null | undefined | Icon or element to display with the title | | title | string | Required | Title of the step | | content | React.ReactNode | Required | Main content of the step | | selector | string | Required | CSS selector for the target element (e.g., '#my-button') | | side | 'top' \| 'bottom' \| 'left' \| 'right' | 'top' | Preferred side for card placement (auto-flips if cutoff detected) | | pointerPadding | number | 10 | Padding around the spotlight (in pixels) | | pointerRadius | number | 8 | Border radius of the spotlight (in pixels) | | nextRoute | string | undefined | Next.js route to navigate to when going to next step | | prevRoute | string | undefined | Next.js route to navigate to when going to previous step | | showControls | boolean | true | Show navigation buttons (prev, next, skip) in default card |

Example:

{
  icon: '🚀',
  title: 'Dashboard Overview',
  content: 'This is your main dashboard where you can see all your metrics.',
  selector: '#dashboard',
  side: 'bottom',
  pointerPadding: 15,
  pointerRadius: 12,
  showControls: true,
}

OnboardingLabels Interface

Customize all text labels for internationalization.

interface OnboardingLabels {
  next?: string;
  previous?: string;
  finish?: string;
  skip?: string;
  of?: string;
  ariaLabels?: {
    closeButton?: string;
    nextButton?: string;
    previousButton?: string;
    finishButton?: string;
    skipButton?: string;
    card?: string;
  };
}

Default Labels (English)

const defaultLabels = {
  next: 'Next',
  previous: 'Previous',
  finish: 'Finish',
  skip: 'Skip Tour',
  of: 'of',
  ariaLabels: {
    closeButton: 'Close onboarding',
    nextButton: 'Go to next step',
    previousButton: 'Go to previous step',
    finishButton: 'Finish onboarding',
    skipButton: 'Skip tour',
    card: 'Onboarding card',
  },
};

Internationalization Example

const norwegianLabels: OnboardingLabels = {
  next: 'Neste',
  previous: 'Forrige',
  finish: 'Fullfør',
  skip: 'Hopp over',
  of: 'av',
  ariaLabels: {
    closeButton: 'Lukk introduksjon',
    nextButton: 'Gå til neste steg',
    previousButton: 'Gå til forrige steg',
    finishButton: 'Fullfør introduksjon',
    skipButton: 'Hopp over tur',
    card: 'Introduksjonskort',
  },
};

<Onboarding steps={tours} labels={norwegianLabels}>
  {children}
</Onboarding>

Custom Card Component

Create your own card component for complete design control.

CardComponentProps Interface

interface CardComponentProps {
  step: Step;
  currentStep: number;
  totalSteps: number;
  nextStep: () => void;
  prevStep: () => void;
  labels?: OnboardingLabels;
}

Example Custom Card

import type { CardComponentProps } from '@selvklart/onboarding';

export function CustomCard({
  step,
  currentStep,
  totalSteps,
  nextStep,
  prevStep,
  labels,
}: CardComponentProps) {
  return (
    <div className="custom-card">
      <h2>
        {step.icon} {step.title}
      </h2>
      <p>{step.content}</p>
      <div className="card-footer">
        <span>
          {currentStep + 1} {labels?.of || 'of'} {totalSteps}
        </span>
        <div>
          {currentStep > 0 && (
            <button onClick={prevStep}>
              {labels?.previous || 'Previous'}
            </button>
          )}
          <button onClick={nextStep}>
            {currentStep === totalSteps - 1
              ? labels?.finish || 'Finish'
              : labels?.next || 'Next'}
          </button>
        </div>
      </div>
    </div>
  );
}

// Use it
<Onboarding steps={tours} cardComponent={CustomCard}>
  {children}
</Onboarding>

Advanced Usage

Multi-Route Tours

Navigate between different pages during a tour using nextRoute and prevRoute:

const tours: Tour[] = [
  {
    tour: 'full-app-tour',
    steps: [
      {
        title: 'Dashboard',
        content: 'This is your dashboard.',
        selector: '#dashboard',
        nextRoute: '/settings', // Navigate to settings on next
      },
      {
        title: 'Settings',
        content: 'Here you can adjust your preferences.',
        selector: '#settings-panel',
        prevRoute: '/', // Navigate back to home on previous
        nextRoute: '/profile',
      },
      {
        title: 'Profile',
        content: 'Manage your profile here.',
        selector: '#profile',
        prevRoute: '/settings',
      },
    ],
  },
];

The library will automatically:

  1. Navigate to the specified route
  2. Wait for the target element to appear in the DOM
  3. Advance to the next step

Programmatic Control

Control tours from anywhere in your app:

function TourControls() {
  const { startOnboarding, closeOnboarding, setCurrentStep, currentStep } = useOnboarding();

  return (
    <div>
      <button onClick={() => startOnboarding('welcome-tour')}>
        Start Welcome Tour
      </button>
      <button onClick={() => startOnboarding('advanced-tour')}>
        Start Advanced Tour
      </button>
      <button onClick={closeOnboarding}>
        Close Tour
      </button>
      <button onClick={() => setCurrentStep(2)}>
        Jump to Step 3
      </button>
      <p>Current Step: {currentStep + 1}</p>
    </div>
  );
}

Custom Spotlight Styling

Customize the spotlight shadow color and opacity:

<Onboarding
  steps={tours}
  shadowRgb="139, 0, 139"  // Dark magenta
  shadowOpacity="0.4"       // 40% opacity
>
  {children}
</Onboarding>

Prevent Interaction with Highlighted Elements

Disable clicks on highlighted elements during the tour:

<Onboarding
  steps={tours}
  interact={false}  // Users cannot click highlighted elements
>
  {children}
</Onboarding>

Custom Animations

Customize card animations using Motion's transition API:

<Onboarding
  steps={tours}
  cardTransition={{
    type: 'spring',
    stiffness: 300,
    damping: 20,
  }}
>
  {children}
</Onboarding>

How It Works

Smart Positioning

The library uses @floating-ui/react-dom to intelligently position cards:

  1. Preferred Side: Cards try to render on the side you specify
  2. Auto-Flip: If the card would be cut off by the viewport, it automatically flips to the opposite side
  3. Shift: Cards shift horizontally/vertically to stay within viewport bounds
  4. Arrow: A dynamic arrow points to the target element based on final position

Spotlight Effect

The spotlight uses a box-shadow technique:

box-shadow: 0 0 200vw 200vh rgba(0, 0, 0, 0.2);

This creates a dark overlay everywhere except the highlighted element.

Confetti Celebration

When a tour is completed, confetti automatically fires with:

  • 100 particles
  • 70-degree spread
  • Respects prefers-reduced-motion

TypeScript Support

All types are exported for your convenience:

import type {
  Tour,
  Step,
  OnboardingLabels,
  CardComponentProps,
} from '@selvklart/onboarding';

Browser Support

This library supports all modern browsers that support:

  • ES Modules
  • CSS Grid
  • Intersection Observer
  • Mutation Observer

Contributing

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


License

MIT License - see LICENSE for details.


Credits