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

user-journey-analytics

v1.0.5

Published

A lightweight user journey tracker for React.js and Next.js applications. Track page flows, user actions, and time spent with optional backend integration. Works with any backend and any database - no analytics tools, cookies, or accounts required.

Readme

user-journey-analytics

npm license

A lightweight user journey tracker for frontend applications. tracks page flows, user actions, and time spent with full analytics calculation. Optionally send data to your backend - no analytics tools, cookies, or accounts required.

What It Does

This package calculates and tracks:

  • Page Navigation - Every route change is tracked
  • User Actions - Track button clicks, form submissions, and custom events
  • Time Spent - Calculation of time spent on each page
  • Session Data - Complete journey data with timestamps and metadata
  • Export Analytics - Export full journey data as JSON for analysis

All analytics are calculated and stored in the browser. Backend integration is completely optional — you can use it purely for client-side analytics or optionally send data to your own backend.


Why user-journey-analytics?

Most analytics tools are:

  • Heavy and bloated
  • Paid subscriptions required
  • Backend-dependent
  • Overkill for dashboards, POS systems, and MVPs

This package is built for developers, QA teams, and product managers who want:

  • Simple setup - One provider, zero configuration
  • Full control - Your data stays in your app
  • Debug-friendly - Export and analyze journey data easily
  • Privacy-first - No cookies, no external tracking
  • Lightweight - Minimal bundle size impact
  • Optional backend - Use client-side only or send to your backend

Features

Core Analytics Features

  • Automatic page tracking - Works with App Router & Pages Router
  • Manual action tracking - Track button clicks, form submissions, custom events
  • Time spent per page - Automatic calculation of user engagement
  • Export journey data - Get full journey as JSON for analysis
  • Session tracking - One journey per browser tab/window
  • Persistence - Journey data survives page refreshes (localStorage)
  • Dev-only mode - Automatically disables in production builds
  • No cookies - Privacy-friendly, no external tracking scripts
  • TypeScript support - Full type definitions included

Optional Backend Integration

  • Backend integration - Send events to your own API (any backend, any database)
  • Event batching - Automatic batching for efficient data transmission
  • Reliable delivery - Uses sendBeacon() for guaranteed delivery on page unload

Installation

npm install user-journey-analytics

or

yarn add user-journey-analytics

or

pnpm add user-journey-analytics

Quick Start

Step 1: Wrap Your App with JourneyProvider

Important: JourneyProvider can be used in server components (no "use client" needed). The useJourney() hook still requires client components (React limitation).

App Router (app/layout.tsx) - Server Component

// No "use client" needed! JourneyProvider works in server components
import { JourneyProvider } from "user-journey-analytics";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <JourneyProvider appName="My App">
          {children}
        </JourneyProvider>
      </body>
    </html>
  );
}

Note: If you're using Next.js App Router, you can use JourneyProvider directly in your server component layout.tsx without the "use client" directive. The provider automatically handles client-side tracking internally.

Pages Router (pages/_app.tsx)

import type { AppProps } from "next/app";
import { usePagesRouter } from "user-journey-analytics";

function MyApp({ Component, pageProps }: AppProps) {
  usePagesRouter({ 
    appName: "My App",
    devOnly: true,
    session: true  // persist defaults to true, no need to specify
  });

  return <Component {...pageProps} />;
}

export default MyApp;

Step 2: Track Actions (Optional)

"use client";

import { useJourney } from "user-journey-analytics";

export default function MyButton() {
  const { trackAction } = useJourney();

  return (
    <button onClick={() => trackAction("Button: Get Started Clicked")}>
      Get Started
    </button>
  );
}

That's it! Page navigation is automatically tracked and analytics are calculated.


Usage Examples

Track Button Clicks

"use client";

import { useJourney } from "user-journey-analytics";

export default function ProductCard() {
  const { trackAction } = useJourney();

  return (
    <button
      onClick={() => trackAction("Product: Add to Cart - Product A")}
    >
      Add to Cart
    </button>
  );
}

Track Form Interactions

"use client";

import { useState } from "react";
import { useJourney } from "user-journey-analytics";

export default function ContactForm() {
  const { trackAction } = useJourney();
  const [email, setEmail] = useState("");

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    trackAction("Form: Contact Submitted");
    // Submit form...
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => {
          setEmail(e.target.value);
          trackAction("Form: Email Field Focused");
        }}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Track Actions with Metadata

Metadata is sent to your backend API (if configured) but is not included in the exported JourneyData format. Use metadata for rich analytics in your backend.

"use client";

import { useJourney } from "user-journey-analytics";

export default function ProductCard({ productId, productName }) {
  const { trackAction } = useJourney();

  const handleAddToCart = () => {
    trackAction("Product: Add to Cart", {
      productId,
      productName,
      price: 29.99,
      category: "electronics",
      timestamp: Date.now(),
      userAgent: navigator.userAgent
    });
  };

  return (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  );
}

Note: Metadata is included in events sent to your backend API, but the exportJourney() function returns the legacy format without metadata. To access metadata, query your backend database.

Export Journey Data

"use client";

import { useJourney } from "user-journey-analytics";

export default function ExportButton() {
  const { exportJourney } = useJourney();

  const handleExport = () => {
    const data = exportJourney();
    
    // Download as JSON file
    const blob = new Blob([JSON.stringify(data, null, 2)], {
      type: "application/json",
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `journey-${Date.now()}.json`;
    a.click();
    URL.revokeObjectURL(url);
  };

  return <button onClick={handleExport}>Export Journey</button>;
}

Export to CSV

You can convert the exported journey data to CSV format and download it as a .csv file for analysis in spreadsheet applications. Note that metadata is not included in the exported data - it's only available via your backend database.

See the demo application for complete CSV export implementation examples.

Clear Journey Data

"use client";

import { useJourney } from "user-journey-analytics";

export default function ClearButton() {
  const { clearJourney } = useJourney();

  const handleClear = () => {
    if (confirm("Are you sure you want to clear all journey data?")) {
      clearJourney();
      console.log("Journey data cleared");
    }
  };

  return <button onClick={handleClear}>Clear Journey</button>;
}

Architecture

Server Component Support

The package now supports Next.js server components through a hybrid architecture:

  • JourneyProvider - Server component compatible wrapper that can be used in server components without "use client"
  • JourneyProviderClient - Internal client component that handles all tracking logic (automatically rendered by JourneyProvider)
  • useJourney() - Client-only hook (React limitation: hooks cannot be used in server components)

This design allows you to:

  • Use JourneyProvider in server component layouts without "use client" directive
  • Maintain backward compatibility with existing code
  • Work seamlessly in both React and Next.js applications

How It Works

Server Component (layout.tsx)
  └─ <JourneyProvider> (server component)
       └─ <JourneyProviderClient> (client component)
            ├─ Tracks page navigation
            ├─ Handles browser APIs
            └─ Manages analytics store

API Reference

JourneyProvider

Server Component Compatible - Can be used in server components (Next.js App Router) without "use client" directive.

Provider component that automatically tracks page navigation and calculates analytics.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | appName | string | undefined | Optional name for your application. Appears in exported journey data. | | devOnly | boolean | false | If true, tracking only works when NODE_ENV === "development". Automatically disables in production builds. | | persist | boolean | true | If true, saves journey data to localStorage. Data survives page refreshes and browser restarts. | | session | boolean | false | If true, uses sessionStorage for one journey per browser tab/window. Data clears when tab is closed. | | storageKey | string | "user-journey-analytics" | Custom key for localStorage/sessionStorage. Use this if you need multiple separate trackers. | | endpoint | string | undefined | (Optional) Backend API endpoint to send events to (e.g., /api/journey). If provided, events are batched and sent to backend. | | apiKey | string | undefined | (Optional) API key for backend authentication. Sent via Authorization: Bearer header or URL param. | | flushInterval | number | 30000 | (Optional) Time in milliseconds between automatic flushes (default: 30 seconds). | | batchSize | number | 10 | (Optional) Number of events to buffer before auto-flushing (default: 10). |

Example

<JourneyProvider
  appName="My E-commerce App"
  devOnly={true}
  session={true}
  storageKey="my-custom-key"
>
  {children}
</JourneyProvider>

Note: persist defaults to true, so journey data is automatically saved to localStorage unless explicitly set to false.

Note: If both persist and session are enabled, sessionStorage takes precedence.


useJourney(devOnly?: boolean)

Hook that provides functions to track actions and manage journey data.

Note: This hook can only be used in client components. Add "use client" to your component file when using this hook (this is a React limitation, not a package limitation).

Returns

An object with the following methods:

  • trackAction(action: string, metadata?: Record<string, unknown>): void - Track a user action with optional metadata. Metadata is sent to backend (if configured) but not included in exported JourneyData (legacy format).
  • exportJourney(): JourneyData - Export full journey data as JSON for analysis. Note: This returns the legacy format without metadata. Metadata is only available via backend events.
  • clearJourney(): void - Reset all journey data
  • flush(): Promise<void> - (Optional) Manually flush pending events to backend (only works if endpoint is configured)

Parameters

  • devOnly (optional): If true, only tracks when NODE_ENV === "development"

Example

"use client";

import { useJourney } from "user-journey-analytics";

export default function MyComponent() {
  const { trackAction, exportJourney, clearJourney } = useJourney();

  const handleClick = () => {
    trackAction("Button: Submit Form Clicked");
  };

  const handleExport = () => {
    const data = exportJourney();
    console.log(data);
    // Export to file, send to server, etc.
  };

  return (
    <div>
      <button onClick={handleClick}>Submit</button>
      <button onClick={handleExport}>Export Journey</button>
      <button onClick={clearJourney}>Clear Data</button>
    </div>
  );
}

usePagesRouter(options)

Hook for Pages Router integration (alternative to JourneyProvider).

Options

Same as JourneyProvider props:

  • appName?: string
  • devOnly?: boolean
  • persist?: boolean - Save to localStorage (default: true)
  • session?: boolean - Use sessionStorage
  • storageKey?: string - Custom storage key
  • endpoint?: string - (Optional) Backend API endpoint
  • apiKey?: string - (Optional) API key for authentication
  • flushInterval?: number - (Optional) Flush interval in milliseconds
  • batchSize?: number - (Optional) Batch size for events

Example

import { usePagesRouter } from "user-journey-analytics";

function MyApp({ Component, pageProps }) {
  usePagesRouter({ 
    appName: "My App",
    devOnly: true,
    session: true  // persist defaults to true
  });

  return <Component {...pageProps} />;
}

JourneyData Type

The exportJourney() function returns a JourneyData object with the following structure:

interface JourneyData {
  appName?: string;                    // App name if provided
  sessionStart: number;                 // Timestamp when session started
  pages: string[];                      // Array of visited page paths
  actions: JourneyAction[];             // Array of tracked actions
  pageVisits: PageVisit[];              // Array of page visits with timing
  timestamps: Record<string, number>;   // Object mapping pages to timestamps
}

interface JourneyAction {
  page: string;                         // Page where action occurred
  action: string;                       // Action description
  time: string;                         // ISO timestamp
  // Note: metadata is sent to backend but not included in exported JourneyData (legacy format)
}

interface PageVisit {
  path: string;                         // Page path
  timestamp: number;                    // Visit timestamp
  timeSpent?: number;                   // Time spent in milliseconds
}

Example Output

{
  "appName": "My App",
  "sessionStart": 1705572191000,
  "pages": ["/", "/about", "/products", "/checkout"],
  "actions": [
    {
      "page": "/products",
      "action": "Button: Add to Cart Clicked",
      "time": "2025-01-18T10:23:11.000Z"
    },
    {
      "page": "/checkout",
      "action": "Form: Payment Submitted",
      "time": "2025-01-18T10:25:30.000Z"
    }
  ],
  "pageVisits": [
    {
      "path": "/",
      "timestamp": 1705572191000,
      "timeSpent": 2500
    },
    {
      "path": "/about",
      "timestamp": 1705572193500,
      "timeSpent": 12000
    },
    {
      "path": "/products",
      "timestamp": 1705572205500,
      "timeSpent": 45000
    },
    {
      "path": "/checkout",
      "timestamp": 1705572250500
    }
  ],
  "timestamps": {
    "/": 1705572191000,
    "/about": 1705572193500,
    "/products": 1705572205500,
    "/checkout": 1705572250500
  }
}

Configuration Options

Dev-Only Mode

Automatically disable tracking in production:

<JourneyProvider devOnly={true}>
  {children}
</JourneyProvider>

When devOnly={true}, tracking only works when NODE_ENV === "development". Perfect for development and QA environments.

Persistence (localStorage)

Journey data is saved to localStorage by default and automatically restored when the page reloads. This allows journey data to survive page refreshes and browser restarts.

To disable persistence:

<JourneyProvider persist={false}>
  {children}
</JourneyProvider>

Session Tracking (sessionStorage)

Track one journey per browser session:

<JourneyProvider session={true}>
  {children}
</JourneyProvider>

Journey data survives page refreshes but is automatically cleared when the browser tab/window is closed.

Custom Storage Key

Use a custom key for multiple trackers:

<JourneyProvider storageKey="my-custom-tracker-key">
  {children}
</JourneyProvider>

Use Cases

  • POS Systems - Track cashier workflows and identify bottlenecks
  • Admin Dashboards - Monitor admin user journeys and feature usage
  • QA Testing - Reproduce bugs with complete user journey data
  • User Flow Debugging - Understand how users navigate your app
  • MVP Analytics - Get insights without third-party analytics tools
  • Internal Tools - Track usage of internal applications
  • A/B Testing - Compare user journeys across different variants

Important Notes

  1. Server Component Support:

    • JourneyProvider can be used in server components (Next.js App Router) without the "use client" directive
    • The useJourney() hook still requires client components because React hooks can only be used in client components (this is a React limitation, not a package limitation)
    • Works seamlessly in both React and Next.js applications
    • Backward compatible: existing code using "use client" with JourneyProvider continues to work
  2. Analytics First: All analytics are calculated and stored in the browser. Backend integration is completely optional.

  3. Metadata Handling:

    • Metadata passed to trackAction() is sent to your backend API (if configured)
    • Metadata is not included in the exported JourneyData (legacy format)
    • To access metadata, query your backend database where events are stored
  4. Flexible Usage: Works great for development, QA, internal tools, and production applications. Use client-side only or optionally send to your backend.

  5. Storage Limitations:

    • localStorage has ~5-10MB limit
    • sessionStorage has ~5-10MB limit
    • Large journeys may hit storage limits
  6. Browser Support: Requires browsers that support:

    • localStorage / sessionStorage
    • ES6+ features
    • Modern JavaScript APIs
    • navigator.sendBeacon() (for backend integration)

Troubleshooting

Tracking not working?

  1. Verify JourneyProvider is set up: Make sure it wraps your app in layout.tsx (works in both server and client components)
  2. Check useJourney hook usage: If using useJourney() hook, ensure "use client" is at the top of your component file (hooks require client components)
  3. Check devOnly mode: If devOnly={true}, ensure NODE_ENV === "development"
  4. Browser console: Check for any warnings or errors

Data not persisting?

  1. Check persist prop: persist defaults to true. If you've set persist={false}, persistence is disabled
  2. Check browser storage: Verify localStorage is available and not disabled
  3. Storage quota: Check if storage quota is exceeded

TypeScript errors?

  1. Install types: Types are included, no additional @types package needed
  2. Check imports: Ensure you're importing from "user-journey-analytics"

Optional: Backend API Integration

Note: Backend integration is completely optional. The package works perfectly fine without it — all analytics are calculated and stored in the browser. You can export and analyze data without any backend.

For production use, you can optionally send events to your own backend API. This follows the same architecture as GA, Segment, and PostHog SDKs.

Architecture Overview

Browser
 ├─ Analytics calculation (always happens)
 ├─ in-memory store
 ├─ localStorage (temporary buffer)
 └─ sendBeacon / fetch (optional)
        ↓
Your Backend API (any language, any framework)
        ↓
Your Database (Postgres, MongoDB, MySQL, SQLite, ClickHouse, etc.)

Key Principles:

  • Analytics are always calculated in the browser
  • Client buffers events and sends in batches (if endpoint provided)
  • Uses navigator.sendBeacon() for reliable delivery
  • localStorage acts as safety buffer, not source of truth
  • Backend is optional — analytics work without it
  • Backend-agnostic - Works with any backend (Node.js, Python, Go, PHP, etc.)
  • Database-agnostic - Works with any database (SQL, NoSQL, etc.)

Quick Setup

1. Configure JourneyProvider with Backend

<JourneyProvider
  appName="My App"
  endpoint="/api/journey"        // Your backend API endpoint
  apiKey="your-api-key"          // Optional: API key for auth
  flushInterval={30000}           // Flush every 30 seconds
  batchSize={10}                  // Flush after 10 events
>
  {children}
</JourneyProvider>

2. Create Backend API Endpoint

Your backend should accept POST requests with this structure:

POST /api/journey
Content-Type: application/json
Authorization: Bearer your-api-key  // Optional

{
  "sessionId": "session-1234567890-abc123",
  "userId": "optional-user-id",
  "appName": "My App",
  "events": [
    {
      "type": "page_view",
      "path": "/home",
      "timestamp": 1705572191000
    },
    {
      "type": "action",
      "path": "/home",
      "action": "Button: Get Started Clicked",
      "timestamp": 1705572192000,
      "metadata": { 
        "buttonId": "get-started",
        "buttonType": "primary",
        "userAgent": "Mozilla/5.0..."
      }
    },
    {
      "type": "page_exit",
      "path": "/home",
      "timestamp": 1705572195000,
      "timeSpent": 4000
    }
  ]
}

Response: Return 200 OK on success.

3. Event Types

type EventType = "page_view" | "action" | "page_exit";

interface JourneyEvent {
  type: EventType;
  path?: string;           // For page_view and page_exit
  action?: string;         // For action events
  timestamp: number;       // Unix timestamp in milliseconds
  timeSpent?: number;      // For page_exit events (milliseconds)
  metadata?: Record<string, unknown>;  // Optional custom data
}

Features

Automatic Batching

  • Events are buffered in memory
  • Sent in batches (configurable batchSize)
  • Reduces server load and improves performance

Smart Flushing

  • Time-based: Flushes every N milliseconds (flushInterval)
  • Count-based: Flushes after N events (batchSize)
  • Visibility change: Flushes when page becomes hidden
  • Page unload: Flushes on beforeunload and pagehide

Reliable Delivery

  • Uses navigator.sendBeacon() (works on tab close, refresh, route change)
  • Falls back to fetch() with keepalive: true
  • Retry queue for failed batches
  • Buffer cleared only after successful send (200 OK)

Manual Flush

"use client";

import { useJourney } from "user-journey-analytics";

export default function FlushButton() {
  const { flush } = useJourney();

  const handleFlush = async () => {
    try {
      await flush();
      console.log("Events flushed successfully");
    } catch (error) {
      console.error("Failed to flush events:", error);
    }
  };

  return <button onClick={handleFlush}>Flush Events</button>;
}

Package Information

  • Package Name: user-journey-analytics
  • Version: 1.04
  • License: MIT
  • Node Version: >=16.0.0
  • Peer Dependencies:
    • next: >=13.0.0
    • react: >=18.0.0

License

MIT © 2025 Nimra Zahoor


Demo

A complete Next.js demo application showcasing all features:

  • GitHub: https://github.com/Nimra-Zahoor/demo-user-journey
  • Features:
    • Automatic page tracking
    • Action tracking with metadata
    • Backend API integration with SQLite
    • Export to JSON, CSV, and PDF
    • Real-time journey viewer
    • Database events viewer
    • Complete implementation examples

Support

If you have questions or need help, please:

  • Open an issue on GitHub
  • Check existing issues and discussions

Made with ❤️ for the Frontend Analytics community