@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
Maintainers
Readme
@shopana/ga
🚀 A modern, type-safe, and performance-optimized Google Analytics 4 (GA4) tracking library for React and Next.js applications.
✨ 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
BrowserGtagAdapterto send events to Google Analytics - Server (SSR): Uses
ServerNoopAdapterwhich 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
BrowserGtagAdapterto send events to Google Analytics - Server (SSR): Uses
ServerNoopAdapterwhich 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 purchasesaddToCart(params)- Track add to cartremoveFromCart(params)- Track remove from cartviewCart(params)- Track cart viewsbeginCheckout(params)- Track checkout startaddPaymentInfo(params)- Track payment infoaddShippingInfo(params)- Track shipping infoviewItem(params)- Track product viewsviewItemList(params)- Track product list viewsselectItem(params)- Track item selectionviewPromotion(params)- Track promotion viewsselectPromotion(params)- Track promotion clicksaddToWishlist(params)- Track wishlist additionsgenerateLead(params)- Track lead generation
Content Events
search(params)- Track searchesshare(params)- Track content sharing
Video Events
videoStart(params)- Track video startvideoProgress(params)- Track video progressvideoComplete(params)- Track video completion
Error & Timing Events
exception(params)- Track exceptions/errorstimingComplete(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 instancedestroy()- Cleanup and destroy tracker resources
AnalyticsClient Methods
init()- Initialize the analytics clienttrack(payload, options?)- Track an eventflush(options?)- Flush queued eventsupdateConfig(patch)- Update configurationgetState()- Get current client stategetDebugChannel()- Get debug channel instancedestroy()- 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.
