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

@shopana/ga

v0.0.1-alpha

Published

Type-safe Google Analytics 4 (GA4) tracking library for React and Next.js with ecommerce support, event batching, and SSR compatibility

Readme

@shopana/ga

🚀 A modern, type-safe, and performance-optimized Google Analytics 4 (GA4) tracking library for React and Next.js applications.

npm version License CI TypeScript

✨ Features

  • 🎯 Type-Safe: Full TypeScript support with comprehensive type definitions
  • Performance Optimized: Automatic event batching and intelligent retry mechanisms
  • 🔄 SSR Ready: Seamless support for Next.js and server-side rendering
  • 🛒 Ecommerce Tracking: Complete GA4 ecommerce event tracking out of the box
  • 🎬 Rich Event Support: Page views, video tracking, content engagement, error tracking, and more
  • 🔍 Debug Mode: Built-in debug channel for development and troubleshooting
  • 🎣 React Hooks: Intuitive React hooks for easy integration
  • 🛡️ Production Ready: Robust error handling and validation
  • 📦 Tree Shakeable: Optimized bundle size with ES modules
  • 🔌 Extensible: Platform adapter pattern for custom implementations

📦 Installation

npm install @shopana/ga
# or
yarn add @shopana/ga
# or
pnpm add @shopana/ga

🚀 Quick Start

React / Next.js Setup

Wrap your application with GAProvider:

import { GAProvider } from "@shopana/ga/react";
import { App } from "./App";

function Root() {
  return (
    <GAProvider
      config={{
        measurementId: "G-XXXXXXXXXX",
      }}
    >
      <App />
    </GAProvider>
  );
}

Track Page Views

import { useGATracker } from "@shopana/ga/react";

function ProductPage() {
  const tracker = useGATracker();

  useEffect(() => {
    tracker.pageView({
      page_title: "Product Page",
      page_location: window.location.href,
      page_path: "/product/123",
    });
  }, []);

  return <div>Product Page</div>;
}

Automatic Page View Tracking (Next.js)

import { useAutoPageView } from "@shopana/ga/react";
import { useRouter } from "next/router";

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  useAutoPageView(router);

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

🔄 Server-Side Rendering (SSR) & Next.js

The library is fully compatible with server-side rendering and Next.js. It automatically detects the environment and uses the appropriate adapter.

How It Works

The library automatically detects whether it's running in a browser or server environment:

  • Browser: Uses BrowserGtagAdapter to send events to Google Analytics
  • Server (SSR): Uses ServerNoopAdapter which safely does nothing, preventing errors

This means you can use the same code in both environments without any additional configuration.

Next.js Setup

Pages Router (Next.js 12 and earlier)

// pages/_app.tsx
import { GAProvider } from "@shopana/ga/react";
import { useRouter } from "next/router";
import { useAutoPageView } from "@shopana/ga/react";

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  useAutoPageView(router);

  return (
    <GAProvider
      config={{
        measurementId: "G-XXXXXXXXXX",
      }}
    >
      <Component {...pageProps} />
    </GAProvider>
  );
}

export default MyApp;

App Router (Next.js 13+)

// app/layout.tsx
"use client";

import { GAProvider } from "@shopana/ga/react";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { useGATracker } from "@shopana/ga/react";

function PageViewTracker() {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const tracker = useGATracker();

  useEffect(() => {
    const url = pathname + (searchParams?.toString() ? `?${searchParams.toString()}` : "");
    tracker.pageView({
      page_path: pathname,
      page_location: typeof window !== "undefined" ? window.location.href : url,
    });
  }, [pathname, searchParams, tracker]);

  return null;
}

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <GAProvider
          config={{
            measurementId: "G-XXXXXXXXXX",
          }}
        >
          <PageViewTracker />
          {children}
        </GAProvider>
      </body>
    </html>
  );
}

SSR Safety

The library is safe to use in SSR environments:

  • ✅ No errors when rendering on the server
  • ✅ Events are only sent from the browser
  • ✅ Automatic environment detection
  • ✅ No hydration mismatches

Manual Adapter Selection

If you need to manually control the adapter (e.g., for testing), you can provide a custom adapter factory:

import { ServerNoopAdapter } from "@shopana/ga";

<GAProvider
  config={{ measurementId: "G-XXXXXXXXXX" }}
  adapterFactory={() => new ServerNoopAdapter()}
>
  {children}
</GAProvider>

📚 Usage Examples

Ecommerce Tracking

import { useGATracker } from "@shopana/ga/react";

function CheckoutButton() {
  const tracker = useGATracker();

  const handlePurchase = async () => {
    await tracker.purchase({
      transaction_id: "T12345",
      value: 29.99,
      currency: "USD",
      items: [
        {
          item_id: "SKU123",
          item_name: "Product Name",
          price: 29.99,
          quantity: 1,
        },
      ],
    });
  };

  return <button onClick={handlePurchase}>Complete Purchase</button>;
}

Add to Cart

const tracker = useGATracker();

tracker.addToCart({
  currency: "USD",
  value: 19.99,
  items: [
    {
      item_id: "SKU456",
      item_name: "Another Product",
      price: 19.99,
      quantity: 1,
    },
  ],
});

Custom Events

const tracker = useGATracker();

tracker.trackEvent("custom_event_name", {
  custom_parameter: "value",
  another_param: 123,
});

Video Tracking

const tracker = useGATracker();

// Video start
tracker.videoStart({
  video_title: "Introduction Video",
  video_url: "https://example.com/video.mp4",
});

// Video progress (e.g., at 25%, 50%, 75%)
tracker.videoProgress({
  video_title: "Introduction Video",
  video_url: "https://example.com/video.mp4",
  video_percent: 50,
});

// Video complete
tracker.videoComplete({
  video_title: "Introduction Video",
  video_url: "https://example.com/video.mp4",
});

Error Tracking

const tracker = useGATracker();

tracker.exception({
  description: "Failed to load user data",
  fatal: false,
});

Authentication Events

const tracker = useGATracker();

// Login
tracker.trackAuth("login", {
  method: "email",
});

// Sign up
tracker.trackAuth("sign_up", {
  method: "google",
});

Engagement Events

const tracker = useGATracker();

// Track user engagement (scroll, click, etc.)
tracker.engagement({
  engagementType: "scroll",
  value: 5000, // milliseconds
});

Timing Events

const tracker = useGATracker();

// Track custom timing events
tracker.timingComplete({
  name: "page_load",
  value: 1200, // milliseconds
  event_category: "performance",
});

⚙️ Configuration

Basic Configuration

<GAProvider
  config={{
    measurementId: "G-XXXXXXXXXX",
    defaultParams: {
      // Global parameters sent with every event
      app_version: "1.0.0",
      environment: "production",
    },
  }}
>
  {children}
</GAProvider>

Advanced Configuration

<GAProvider
  config={{
    measurementId: "G-XXXXXXXXXX",
    dataLayerName: "dataLayer", // Custom dataLayer name
    disabled: false, // Set to true to disable tracking
    defaultParams: {
      // Default parameters for all events
    },
    features: {
      batching: {
        enabled: true, // Enable event batching
        size: 10, // Batch size
        timeoutMs: 5000, // Batch timeout in milliseconds
      },
      retries: {
        enabled: true, // Enable retry on failure
        maxAttempts: 3, // Maximum retry attempts
        delayMs: 1000, // Delay between retries
        jitterRatio: 0.3, // Jitter ratio for exponential backoff
      },
    },
  }}
  hooks={{
    onReady: () => console.log("Analytics ready"),
    onEvent: (payload) => console.log("Event tracked:", payload),
    onError: (error) => console.error("Analytics error:", error),
    onFlush: (count) => console.log(`Flushed ${count} events`),
  }}
>
  {children}
</GAProvider>

Custom Platform Adapter

By default, GAProvider uses createPlatformAdapter() which automatically detects the environment:

  • Browser: Uses BrowserGtagAdapter to send events to Google Analytics
  • Server (SSR): Uses ServerNoopAdapter which safely does nothing

You can provide a custom platform adapter for advanced use cases:

import { type GAPlatformAdapter } from "@shopana/ga";

class CustomAdapter implements GAPlatformAdapter {
  async load(config) {
    // Custom initialization logic
  }

  isReady() {
    return true;
  }

  async send(payload) {
    // Custom event sending logic
  }

  destroy(config) {
    // Custom cleanup logic
  }
}

<GAProvider
  config={{ measurementId: "G-XXXXXXXXXX" }}
  adapterFactory={() => new CustomAdapter()}
>
  {children}
</GAProvider>

🎣 React Hooks

useGATracker()

Returns the tracker instance for tracking events.

const tracker = useGATracker();

useAnalyticsClient()

Returns the underlying analytics client for advanced usage.

const client = useAnalyticsClient();

// Get current state
const state = client.getState();

// Manually flush events
await client.flush({ force: true });

// Update configuration
await client.updateConfig({
  disabled: true,
});

useAutoPageView(router?)

Automatically tracks page views on route changes. Works with Next.js router.

import { useRouter } from "next/router";
import { useAutoPageView } from "@shopana/ga/react";

function App() {
  const router = useRouter();
  useAutoPageView(router);
  // Page views are now tracked automatically
}

useGADebugStream(limit?)

Subscribe to analytics debug events for development and debugging. This hook provides real-time visibility into all analytics events, errors, and system state changes happening in your application.

Use cases:

  • Development debugging: See all tracked events in real-time without checking Google Analytics console
  • Event validation: Verify that events are sent with correct parameters and at the right time
  • Error monitoring: Catch and display analytics errors immediately during development
  • Testing: Ensure analytics integration works correctly before deploying to production
  • Performance monitoring: Track when events are flushed and how many are sent at once
import { useGADebugStream } from "@shopana/ga/react";

function DebugPanel() {
  const events = useGADebugStream(50); // Last 50 events

  return (
    <div>
      <h3>Analytics Debug Stream ({events.length} events)</h3>
      {events.map((event, i) => (
        <div key={i}>
          {event.type === 'event' && (
            <div>📊 Event: {event.payload.name}</div>
          )}
          {event.type === 'error' && (
            <div>❌ Error: {event.error.message}</div>
          )}
          {event.type === 'flush' && (
            <div>✅ Flushed {event.count} events</div>
          )}
          {event.type === 'ready' && (
            <div>🟢 Analytics ready</div>
          )}
        </div>
      ))}
    </div>
  );
}

Direct Context Access

For advanced use cases, you can access the analytics context directly:

import { useContext } from "react";
import { GAContext } from "@shopana/ga/react";

function CustomComponent() {
  const context = useContext(GAContext);

  if (!context) {
    throw new Error("Must be used within GAProvider");
  }

  const { client, tracker, debugChannel } = context;
  // Use client, tracker, or debugChannel directly
}

🔧 Standalone Usage (Without React)

The library automatically detects the environment and uses the appropriate adapter. In browser environments, it uses BrowserGtagAdapter; in server-side environments (SSR), it automatically uses ServerNoopAdapter to prevent errors.

import {
  AnalyticsClient,
  createPlatformAdapter,
  GATracker,
} from "@shopana/ga";

// Automatically selects the correct adapter based on environment
const adapter = createPlatformAdapter();
const client = new AnalyticsClient(adapter, {
  measurementId: "G-XXXXXXXXXX",
});

await client.init();

const tracker = new GATracker(client);

// Track events
await tracker.pageView({
  page_title: "Home",
  page_path: "/",
});

// Cleanup
client.destroy();

Manual Adapter Selection

If you need to manually specify an adapter (e.g., for testing or custom implementations):

import {
  AnalyticsClient,
  BrowserGtagAdapter,
  ServerNoopAdapter,
  GATracker,
} from "@shopana/ga";

// Use browser adapter explicitly
const browserAdapter = new BrowserGtagAdapter();
const client = new AnalyticsClient(browserAdapter, {
  measurementId: "G-XXXXXXXXXX",
});

// Or use server adapter for SSR/testing
const serverAdapter = new ServerNoopAdapter();
const serverClient = new AnalyticsClient(serverAdapter, {
  measurementId: "G-XXXXXXXXXX",
});

📖 API Reference

GATracker Methods

Page Tracking

  • pageView(params) - Track page views

Ecommerce Events

  • purchase(params) - Track purchases
  • addToCart(params) - Track add to cart
  • removeFromCart(params) - Track remove from cart
  • viewCart(params) - Track cart views
  • beginCheckout(params) - Track checkout start
  • addPaymentInfo(params) - Track payment info
  • addShippingInfo(params) - Track shipping info
  • viewItem(params) - Track product views
  • viewItemList(params) - Track product list views
  • selectItem(params) - Track item selection
  • viewPromotion(params) - Track promotion views
  • selectPromotion(params) - Track promotion clicks
  • addToWishlist(params) - Track wishlist additions
  • generateLead(params) - Track lead generation

Content Events

  • search(params) - Track searches
  • share(params) - Track content sharing

Video Events

  • videoStart(params) - Track video start
  • videoProgress(params) - Track video progress
  • videoComplete(params) - Track video completion

Error & Timing Events

  • exception(params) - Track exceptions/errors
  • timingComplete(params) - Track timing events

Authentication Events

  • trackAuth(name, params) - Track login/sign_up events

Engagement Events

  • engagement(params) - Track engagement events

Custom Events

  • trackEvent(name, params, options?) - Track custom events

Utility Methods

  • getAnalyticsClient() - Get the underlying analytics client instance
  • destroy() - Cleanup and destroy tracker resources

AnalyticsClient Methods

  • init() - Initialize the analytics client
  • track(payload, options?) - Track an event
  • flush(options?) - Flush queued events
  • updateConfig(patch) - Update configuration
  • getState() - Get current client state
  • getDebugChannel() - Get debug channel instance
  • destroy() - Cleanup and destroy client

🎯 TypeScript Support

Full TypeScript support with comprehensive type definitions. All event parameters are typed according to GA4 specifications.

Event Parameter Types

import type {
  PurchaseEventParams,
  PageEventParams,
  VideoEventParams,
  CartEventParams,
  EngagementEventParams,
  ExceptionEventParams,
  TimingEventParams,
  AuthEventParams,
} from "@shopana/ga";

const purchaseParams: PurchaseEventParams = {
  transaction_id: "T123",
  value: 29.99,
  currency: "USD",
  // TypeScript will autocomplete and validate all fields
};

Configuration Types

import type {
  GA4Config,
  GA4Features,
  AnalyticsClientHooks,
  EventPayload,
  TrackOptions,
} from "@shopana/ga";

const config: GA4Config = {
  measurementId: "G-XXXXXXXXXX",
  features: {
    batching: { enabled: true, size: 10 },
  },
};

Platform Adapter Types

The library provides two built-in adapters that are automatically selected based on the environment:

import type {
  GAPlatformAdapter,
  GAPlatformAdapterFactory,
} from "@shopana/ga";
import {
  BrowserGtagAdapter,
  ServerNoopAdapter,
  createPlatformAdapter,
} from "@shopana/ga";

// BrowserGtagAdapter - used automatically in browser environments
// ServerNoopAdapter - used automatically in SSR/server environments
// createPlatformAdapter() - automatically selects the correct adapter

class MyAdapter implements GAPlatformAdapter {
  // Implement adapter interface
}

🛡️ Error Handling

The library includes robust error handling:

  • Automatic retry with exponential backoff
  • Validation of event names and parameters
  • Graceful degradation when analytics is disabled
  • Error hooks for custom error handling
<GAProvider
  config={{ measurementId: "G-XXXXXXXXXX" }}
  hooks={{
    onError: (error) => {
      // Handle errors (e.g., send to error tracking service)
      console.error("Analytics error:", error);
    },
  }}
>
  {children}
</GAProvider>

🚫 Disabling Analytics

You can disable analytics in several ways:

// Via configuration
<GAProvider
  config={{
    measurementId: "G-XXXXXXXXXX",
    disabled: true, // Disables all tracking
  }}
>
  {children}
</GAProvider>

// Or dynamically
const client = useAnalyticsClient();
await client.updateConfig({ disabled: true });

🔍 Debugging

The library provides built-in debugging capabilities to help you develop and troubleshoot analytics integration.

Real-time Event Stream

Use useGADebugStream to see all analytics events, errors, and system state changes in real-time. This is especially useful during development when you need to:

  • Verify event tracking: See exactly what events are being sent and with what parameters
  • Debug integration issues: Catch errors immediately without waiting for Google Analytics to process data
  • Test event flow: Understand the sequence of events and when they're triggered
  • Validate configuration: Ensure your analytics setup is working correctly before production
import { useGADebugStream } from "@shopana/ga/react";

function DebugView() {
  const events = useGADebugStream(100); // Keep last 100 events

  return (
    <div style={{ position: 'fixed', bottom: 0, right: 0, maxWidth: '400px' }}>
      <h3>Analytics Events ({events.length})</h3>
      <div style={{ maxHeight: '400px', overflow: 'auto' }}>
        {events.map((event, i) => (
          <div key={i} style={{ marginBottom: '8px', padding: '8px', background: '#f5f5f5' }}>
            {event.type === 'event' && (
              <>
                <strong>📊 Event:</strong> {event.payload.name}
                <pre>{JSON.stringify(event.payload.params, null, 2)}</pre>
              </>
            )}
            {event.type === 'error' && (
              <>
                <strong>❌ Error:</strong> {event.error.message}
                <pre>{event.error.stack}</pre>
              </>
            )}
            {event.type === 'flush' && (
              <strong>✅ Flushed {event.count} events</strong>
            )}
            {event.type === 'ready' && (
              <strong>🟢 Analytics client ready</strong>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

Conditional Debug Panel

You can conditionally show the debug panel only in development:

function App() {
  const isDevelopment = process.env.NODE_ENV === 'development';

  return (
    <GAProvider config={{ measurementId: "G-XXXXXXXXXX" }}>
      <YourApp />
      {isDevelopment && <DebugView />}
    </GAProvider>
  );
}

📦 Bundle Size

The library is optimized for minimal bundle size:

  • Tree-shakeable exports
  • No unnecessary dependencies
  • ES modules support

🤝 Contributing

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

📄 License

Licensed under the Apache License 2.0. See LICENSE for more information.

🔗 Links

🙏 Acknowledgments

Built with ❤️ by the Shopana team.