npm package: @shopana/ga
Description: Type-safe Google Analytics 4 (GA4) tracking library for React and Next.js with ecommerce support, event batching, and SSR compatibility
Author: Shopana
Latest version: 0.0.1-alpha

# @shopana/ga

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

[![npm version](https://img.shields.io/npm/v/@shopana/ga.svg)](https://www.npmjs.com/package/@shopana/ga)
[![License](https://img.shields.io/npm/l/@shopana/ga.svg)](https://github.com/shopanaio/ga/blob/main/LICENSE)
[![CI](https://github.com/shopanaio/ga/actions/workflows/ci.yml/badge.svg)](https://github.com/shopanaio/ga/actions/workflows/ci.yml)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)

## ✨ 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

```bash
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`:

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

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

### Track Page Views

```tsx
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)

```tsx
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)

```tsx
// 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+)

```tsx
// 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:

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

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

## 📚 Usage Examples

### Ecommerce Tracking

```tsx
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

```tsx
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

```tsx
const tracker = useGATracker();

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

### Video Tracking

```tsx
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

```tsx
const tracker = useGATracker();

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

### Authentication Events

```tsx
const tracker = useGATracker();

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

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

### Engagement Events

```tsx
const tracker = useGATracker();

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

### Timing Events

```tsx
const tracker = useGATracker();

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

## ⚙️ Configuration

### Basic Configuration

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

### Advanced Configuration

```tsx
<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:

```tsx
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.

```tsx
const tracker = useGATracker();
```

### `useAnalyticsClient()`

Returns the underlying analytics client for advanced usage.

```tsx
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.

```tsx
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

```tsx
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:

```tsx
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.

```typescript
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):

```typescript
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

```typescript
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

```typescript
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:

```typescript
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

```tsx
<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:

```tsx
// 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

```tsx
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:

```tsx
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](LICENSE) for more information.

## 🔗 Links

- [GitHub Repository](https://github.com/shopanaio/ga)
- [Issue Tracker](https://github.com/shopanaio/ga/issues)
- [npm Package](https://www.npmjs.com/package/@shopana/ga)

## 🙏 Acknowledgments

Built with ❤️ by the Shopana team.