UNPKG

@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
# @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.