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

@startupkit/analytics

v0.5.4

Published

Provider-agnostic analytics helpers for StartupKit

Readme

@startupkit/analytics

Provider-agnostic analytics helpers for StartupKit projects.

Part of StartupKit - The Zero to One Startup Framework.

Installation

pnpm add @startupkit/analytics

Or use the StartupKit CLI to get started with a complete monorepo setup:

npx startupkit init

What This Package Provides

This package provides a provider-agnostic analytics context pattern with built-in conveniences:

  • Plugin architecture - Compose multiple analytics providers
  • Auto page tracking (with Next.js App Router)
  • Memoized handlers for performance
  • Type-safe hooks (useAnalytics, useFlag)
  • No vendor lock-in - use any analytics providers

Built-in Plugins

The package includes ready-to-use plugins for popular analytics services:

  • PostHog - Product analytics and feature flags
  • GoogleAnalytics - Google Analytics 4 integration
  • OpenPanelPlugin - Privacy-focused analytics
  • AhrefsPlugin - SEO and traffic analytics

Usage

Plugin Mode (Recommended)

The plugin architecture makes it easy to use multiple analytics providers:

"use client"

import {
  AnalyticsProvider,
  PostHogPlugin,
  GoogleAnalyticsPlugin,
  OpenPanelPlugin
} from "@startupkit/analytics"

const plugins = [
  PostHogPlugin({
    apiKey: process.env.NEXT_PUBLIC_POSTHOG_API_KEY,
    apiHost: process.env.NEXT_PUBLIC_POSTHOG_HOST // optional
  }),
  GoogleAnalyticsPlugin({
    measurementId: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID
  }),
  OpenPanelPlugin({
    clientId: process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID
  })
]

export function Providers({ children, flags }) {
  return (
    <AnalyticsProvider flags={flags} plugins={plugins}>
      {children}
    </AnalyticsProvider>
  )
}

Using Analytics in Components

"use client"

import { useAnalytics } from "@startupkit/analytics"

export function MyComponent() {
  const { track, identify, page, reset, flags } = useAnalytics()

  const handleClick = () => {
    track("BUTTON_CLICKED", { 
      buttonId: "cta",
      page: "home" 
    })
  }

  const handleSignIn = (userId: string, email: string) => {
    identify(userId, {
      email,
      plan: "pro"
    })
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  )
}

Feature Flags

import { useFlag } from "@startupkit/analytics"

export function MyFeature() {
  const isEnabled = useFlag("new-feature")

  if (!isEnabled) {
    return null
  }

  return <div>New Feature!</div>
}

API Reference

AnalyticsProvider

Provider component that accepts flags and plugins or handlers.

interface AnalyticsProviderProps<TFlags> {
  flags: TFlags
  plugins?: AnalyticsPlugin[]
  handlers?: AnalyticsHandlers
  autoPageTracking?: boolean  // Default: true
  children: ReactNode
}

Plugin Mode (Recommended):

<AnalyticsProvider 
  flags={flags} 
  plugins={[
    PostHogPlugin({ apiKey: "..." }),
    GoogleAnalyticsPlugin({ measurementId: "..." })
  ]}
>
  {children}
</AnalyticsProvider>

Custom Handlers with Plugins (Advanced):

For full customization, provide both plugins and handlers. Your handlers receive a strongly-typed plugins object:

const plugins = [
  PostHogPlugin({ apiKey: "..." }),
  GoogleAnalyticsPlugin({ measurementId: "..." })
] as const // Important: use 'as const' for type inference

<AnalyticsProvider 
  flags={flags}
  plugins={plugins}
  handlers={{
    identify: ({ plugins, userId, traits }) => {
      // plugins.PostHog is strongly typed!
      if (userId) {
        plugins.PostHog.identify?.(userId, traits)
        
        // Add custom logic
        if (traits?.isPremium) {
          plugins.GoogleAnalytics.identify?.(userId, traits)
        }
      }
    },
    track: ({ plugins, event, properties }) => {
      // Full control over which events go where
      plugins.PostHog.track?.(event, properties)
      
      if (event.startsWith('PURCHASE_')) {
        plugins.GoogleAnalytics.track?.(event, properties)
      }
    },
    page: ({ plugins, name, properties }) => {
      plugins.PostHog.page?.(name, properties)
      plugins.GoogleAnalytics.page?.(name, properties)
    },
    reset: ({ plugins }) => {
      plugins.PostHog.reset?.()
      plugins.GoogleAnalytics.reset?.()
    }
  }}
>
  {children}
</AnalyticsProvider>

Simple Handlers Mode (No Plugins):

<AnalyticsProvider 
  flags={flags}
  handlers={{
    identify: (userId, traits) => { /* your code */ },
    track: (event, properties) => { /* your code */ },
    page: (name, properties) => { /* your code */ },
    reset: () => { /* your code */ }
  }}
>
  {children}
</AnalyticsProvider>

Auto Page Tracking:

  • Automatically tracks page views using Next.js App Router hooks
  • Filters out route groups like (dashboard)
  • Replaces long numeric segments with :id
  • Can be disabled with autoPageTracking: false

useAnalytics()

Hook to access analytics context.

const { identify, track, page, reset, flags } = useAnalytics()

// Identify a user
identify("user_123", {
  email: "[email protected]",
  plan: "pro"
})

// Track an event
track("PURCHASE_COMPLETED", {
  amount: 99.99,
  currency: "USD"
})

// Track a page view
page("/dashboard", { 
  pathname: "/dashboard/settings" 
})

// Reset analytics (on logout)
reset()

useFlag(name)

Hook to access a specific feature flag.

const isNewFeatureEnabled = useFlag("new-feature")

if (isNewFeatureEnabled) {
  // Show new feature
}

Built-in Plugins

PostHog

import { PostHogPlugin } from "@startupkit/analytics"

const plugin = PostHogPlugin({
  apiKey: "phc_...",
  apiHost: "https://app.posthog.com" // optional
})

Features:

  • Product analytics
  • Feature flags
  • Session recordings
  • A/B testing

GoogleAnalytics

import { GoogleAnalyticsPlugin } from "@startupkit/analytics"

const plugin = GoogleAnalyticsPlugin({
  measurementId: "G-XXXXXXXXXX"
})

Features:

  • Page view tracking
  • Event tracking
  • Google Analytics 4 integration

OpenPanelPlugin

import { OpenPanelPlugin } from "@startupkit/analytics"

const plugin = OpenPanelPlugin({
  clientId: "your-client-id"
})

Features:

  • Privacy-focused analytics
  • Real-time dashboards
  • Event tracking

AhrefsPlugin

import { AhrefsPlugin } from "@startupkit/analytics"

const plugin = AhrefsPlugin()

Features:

  • SEO tracking
  • Traffic source analytics

Creating Custom Plugins

You can create your own analytics plugins:

import type { AnalyticsPlugin } from "@startupkit/analytics"
import { useCallback } from "react"

export function MyAnalyticsPlugin(): AnalyticsPlugin {
  return {
    name: "MyAnalytics",
    Provider: ({ children }) => {
      // Optional: Wrap in your provider
      return <>{children}</>
    },
    useHandlers: () => {
      const identify = useCallback(
        (userId: string | null, traits?: Record<string, unknown>) => {
          // Your identify logic
          console.log("Identify:", userId, traits)
        },
        []
      )

      const track = useCallback(
        (event: string, properties?: Record<string, unknown>) => {
          // Your track logic
          console.log("Track:", event, properties)
        },
        []
      )

      const page = useCallback(
        (name?: string, properties?: Record<string, unknown>) => {
          // Your page view logic
          console.log("Page:", name, properties)
        },
        []
      )

      const reset = useCallback(() => {
        // Your reset logic
        console.log("Reset")
      }, [])

      return {
        identify,
        track,
        page,
        reset
      }
    }
  }
}

Advanced: Custom Handlers with Plugins

For maximum flexibility, you can provide both plugins and handlers to gain full control over how analytics events are dispatched. When you do this, the analytics provider:

  1. Initializes all plugins (calls their Provider components)
  2. Gets handlers from each plugin (calls their useHandlers() hooks)
  3. Passes a strongly-typed plugins object to your custom handlers
  4. Lets you decide which events go to which plugins

Use Cases

  • Conditional tracking: Only send certain events to specific providers
  • Data transformation: Modify event properties per provider
  • Performance: Skip expensive tracking for non-critical events
  • Debugging: Log events before sending to providers
  • A/B testing: Route events differently for different user segments

Example: Conditional Tracking

const plugins = [
  PostHogPlugin({ apiKey: "..." }),
  GoogleAnalyticsPlugin({ measurementId: "..." })
] as const // 'as const' enables type inference

<AnalyticsProvider 
  flags={flags}
  plugins={plugins}
  handlers={{
    track: ({ plugins, event, properties }) => {
      // All events go to PostHog
      plugins.PostHog.track?.(event, properties)
      
      // Only send purchase events to Google Analytics
      if (event.startsWith('PURCHASE_')) {
        plugins.GoogleAnalytics.track?.(event, {
          ...properties,
          value: properties.amount // Transform property name
        })
      }
    },
    identify: ({ plugins, userId, traits }) => {
      if (!userId) return
      
      plugins.PostHog.identify?.(userId, traits)
      
      // Only identify premium users in GA
      if (traits?.plan === 'premium') {
        plugins.GoogleAnalytics.identify?.(userId, traits)
      }
    },
    page: ({ plugins, name, properties }) => {
      // Track all page views in both
      plugins.PostHog.page?.(name, properties)
      plugins.GoogleAnalytics.page?.(name, properties)
    },
    reset: ({ plugins }) => {
      plugins.PostHog.reset?.()
      plugins.GoogleAnalytics.reset?.()
    }
  }}
>
  {children}
</AnalyticsProvider>

Type Safety

The plugins object is strongly typed based on the plugins you provide:

const plugins = [
  PostHogPlugin({ apiKey: "..." }),
  GoogleAnalyticsPlugin({ measurementId: "..." })
] as const // Important!

// TypeScript knows these exist:
plugins.PostHog.track(...)      // ✅
plugins.GoogleAnalytics.track(...) // ✅

// TypeScript catches typos:
plugins.PostHOG.track(...)      // ❌ Error
plugins.Mixpanel.track(...)     // ❌ Error

Important: Use as const when defining your plugins array for proper type inference.

Example: Debugging

<AnalyticsProvider 
  flags={flags}
  plugins={plugins}
  handlers={{
    track: ({ plugins, event, properties }) => {
      // Log all events in development
      if (process.env.NODE_ENV === 'development') {
        console.log('📊 Analytics:', event, properties)
      }
      
      // Send to all providers
      plugins.PostHog.track?.(event, properties)
      plugins.GoogleAnalytics.track?.(event, properties)
    }
    // identify, page, reset use default behavior (send to all plugins)
  }}
>
  {children}
</AnalyticsProvider>

Partial Handlers

You don't need to provide all handlers! Unspecified handlers will use the default behavior (sending to all plugins):

const plugins = [
  PostHogPlugin({ apiKey: "..." }),
  GoogleAnalyticsPlugin({ measurementId: "..." })
] as const

<AnalyticsProvider 
  flags={flags}
  plugins={plugins}
  handlers={{
    // Only override track - everything else uses defaults
    track: ({ plugins, event, properties }) => {
      // Custom logic for tracking only
      if (event.startsWith('PURCHASE_')) {
        plugins.PostHog.track?.(event, properties)
        plugins.GoogleAnalytics.track?.(event, properties)
      } else {
        // Only track non-purchase events to PostHog
        plugins.PostHog.track?.(event, properties)
      }
    }
    // identify, page, reset automatically send to ALL plugins
  }}
>
  {children}
</AnalyticsProvider>

This is perfect for:

  • Selective tracking: Override just track to filter which events go where
  • Custom identification: Override just identify to add extra logic
  • Debugging specific methods: Add logging to one method without affecting others

Multi-Provider Setup

Events are sent to all configured plugins simultaneously:

const plugins = [
  PostHogPlugin({ apiKey: "..." }),      // Product analytics
  GoogleAnalyticsPlugin({ measurementId: "..." }), // Marketing analytics
  OpenPanelPlugin({ clientId: "..." })   // Privacy-focused analytics
]

<AnalyticsProvider flags={flags} plugins={plugins}>
  {children}
</AnalyticsProvider>

When you call track(), the event is sent to all three providers automatically.

Auto Page Tracking Details

The package automatically tracks page views with clean route names:

Input: /dashboard/(settings)/profile/abc123def456 Output: /dashboard/profile/:id

  • Filters out route groups: (settings) → removed
  • Replaces long IDs: abc123def456:id
  • Preserves meaningful segments: dashboard, profile

The page handler is called with:

  • name: The clean route (e.g., /dashboard/profile/:id)
  • properties.pathname: The actual pathname

TypeScript Types

// Plugin interface
interface AnalyticsPlugin {
  name: string
  Provider?: ComponentType<{ children: ReactNode }>
  useHandlers: () => Partial<AnalyticsHandlers>
}

// Simple handlers interface (no plugins)
interface AnalyticsHandlers {
  identify: (userId: string | null, traits?: Record<string, unknown>) => void
  track: (event: string, properties?: Record<string, unknown>) => void
  page: (name?: string, properties?: Record<string, unknown>) => void
  reset: () => void
}

// Custom handlers interface (with plugins) - receives plugins object and parameters
interface CustomAnalyticsHandlers<TPlugins extends readonly AnalyticsPlugin[]> {
  identify: (params: {
    plugins: PluginsToHandlersMap<TPlugins>
    userId: string | null
    traits?: Record<string, unknown>
  }) => void
  track: (params: {
    plugins: PluginsToHandlersMap<TPlugins>
    event: string
    properties?: Record<string, unknown>
  }) => void
  page: (params: {
    plugins: PluginsToHandlersMap<TPlugins>
    name?: string
    properties?: Record<string, unknown>
  }) => void
  reset: (params: { 
    plugins: PluginsToHandlersMap<TPlugins> 
  }) => void
}

// Plugins map - strongly typed based on plugin names
type PluginsToHandlersMap<TPlugins extends readonly AnalyticsPlugin[]> = {
  [K in TPlugins[number]["name"]]: Partial<AnalyticsHandlers>
}

// Context interface
interface AnalyticsContextType<TFlags> {
  flags: TFlags
  identify: (userId: string | null, traits?: Record<string, unknown>) => void
  track: (event: string, properties?: Record<string, unknown>) => void
  page: (name?: string, properties?: Record<string, unknown>) => void
  reset: () => void
}

Philosophy

This package follows the StartupKit minimal package philosophy:

What it does:

  • Provides reusable React patterns for analytics
  • Handles auto page tracking (filters route groups, replaces IDs)
  • Memoizes handlers for performance
  • Includes ready-to-use plugins for popular services

What it doesn't do:

  • Force you to use specific analytics providers
  • Make decisions about your analytics architecture

You can use the built-in plugins, create your own, or implement custom handlers.

Peer Dependencies

  • next >= 14.0.0 (for App Router hooks)
  • react >= 18.2.0

Plugin-specific dependencies (only install what you use):

  • posthog-js - For PostHogPlugin
  • @openpanel/sdk - For OpenPanelPlugin
  • (GoogleAnalyticsPlugin and AhrefsPlugin work via script injection)

Learn More

Support

Having issues? Open an issue on GitHub

License

ISC © 2025 01 Studio