@shopana/ga
Version:
Type-safe Google Analytics 4 (GA4) tracking library for React and Next.js with ecommerce support, event batching, and SSR compatibility
853 lines (667 loc) • 20.7 kB
Markdown
# @shopana/ga
> 🚀 A modern, type-safe, and performance-optimized Google Analytics 4 (GA4) tracking library for React and Next.js applications.
[](https://www.npmjs.com/package/@shopana/ga)
[](https://github.com/shopanaio/ga/blob/main/LICENSE)
[](https://github.com/shopanaio/ga/actions/workflows/ci.yml)
[](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.