Best Practices
This guide covers patterns and recommendations for implementing Campaign Cart analytics across your application.
Configuration Best Practices
Section titled “Configuration Best Practices”1. Use Auto Mode
Section titled “1. Use Auto Mode”Auto mode handles most tracking automatically:
window.nextConfig = { apiKey: 'your-api-key', analytics: { enabled: true, mode: 'auto' // Automatic tracking }};Auto Mode Tracks:
dl_user_data- Every page loaddl_view_item_list- Collection/category pagesdl_add_to_cart- Cart additionsdl_begin_checkout- Checkout initiation
Manual Tracking Required:
- Purchase events (order confirmation page)
- Login/signup events
- Custom business events
2. Set storeName for Facebook Pixel
Section titled “2. Set storeName for Facebook Pixel”The storeName parameter is required for Facebook Pixel purchase deduplication. Without it, duplicate purchases may be reported:
window.nextConfig = { apiKey: 'your-api-key', storeName: 'my-store', // Required for Facebook deduplication analytics: { enabled: true, providers: { facebook: { enabled: true, settings: { pixelId: 'YOUR_PIXEL_ID' } } } }};Use a consistent, unique store identifier across all environments to prevent duplicate purchase attribution in Facebook Ads Manager.
3. Use Debug Mode in Development
Section titled “3. Use Debug Mode in Development”Enable debug mode only during development to see detailed console logs without polluting production data:
window.nextConfig = { apiKey: 'your-api-key', analytics: { enabled: true, debug: process.env.NODE_ENV === 'development' // Enable only in dev }};Debug Mode Shows:
- Event names and data
- Provider routing decisions
- Validation errors
- Transform function results
4. Environment-Specific Configuration
Section titled “4. Environment-Specific Configuration”Use environment variables to manage different configurations across environments:
window.nextConfig = { apiKey: process.env.REACT_APP_API_KEY, analytics: { enabled: process.env.REACT_APP_ANALYTICS_ENABLED === 'true', debug: process.env.NODE_ENV === 'development', mode: 'auto', providers: { gtm: { enabled: true }, facebook: { enabled: true, settings: { pixelId: process.env.REACT_APP_FACEBOOK_PIXEL_ID } } } }};Event Naming Conventions
Section titled “Event Naming Conventions”Use Consistent snake_case Format
Section titled “Use Consistent snake_case Format”Adopt a consistent naming convention across all custom events. Use snake_case (lowercase with underscores):
// Good - Consistent snake_case'newsletter_subscribe''video_completed''form_submitted''feature_enabled''product_reviewed''wishlist_added'
// Avoid - Inconsistent formats'subscribeNewsletter' // camelCase'VideoCompleted' // PascalCase'form-submitted' // kebab-case'FEATURE_ENABLED' // UPPER_CASENaming Patterns by Category
Section titled “Naming Patterns by Category”Follow these naming patterns to make event names self-documenting:
// Format: [verb]_[noun]'newsletter_subscribe''video_play''form_submit''product_add''account_create''payment_process'// Format: [noun]_[status]'video_started''video_completed''download_finished''upload_error''sync_failed'// Format: [noun]_[verb]_[context]'product_view_search''product_add_recommendation''video_complete_onboarding''form_submit_footer''checkout_error_payment'Tracking Patterns
Section titled “Tracking Patterns”1. Track Purchase Events ASAP
Section titled “1. Track Purchase Events ASAP”Track purchase events on the order confirmation page immediately when the page loads, before users navigate away:
// On order confirmation pagewindow.addEventListener('DOMContentLoaded', () => { // Get order data from page or API const orderData = window.__ORDER_DATA__ || getOrderFromAPI();
if (orderData) { // Track immediately next.trackPurchase({ id: orderData.orderId, total: orderData.total, currency: orderData.currency, items: orderData.items }); }});Users often navigate away quickly. Track immediately to capture the event before they leave.
2. Track Before Navigation
Section titled “2. Track Before Navigation”For events that might cause navigation, track before the navigation occurs:
// Bad - Event might not send before navigationdocument.getElementById('checkout-btn').addEventListener('click', () => { next.trackBeginCheckout(); window.location.href = '/checkout'; // May interrupt tracking});
// Good - Track with callbackdocument.getElementById('checkout-btn').addEventListener('click', (e) => { e.preventDefault(); next.trackBeginCheckout(); // Give the event time to send setTimeout(() => { window.location.href = '/checkout'; }, 100);});
// Better - Use async trackingdocument.getElementById('checkout-btn').addEventListener('click', async (e) => { e.preventDefault(); await window.NextAnalytics.trackAsync({ event: 'checkout_initiated', from_page: window.location.pathname }); window.location.href = '/checkout';});3. Include Context in Events
Section titled “3. Include Context in Events”Provide context data with custom events:
// Good - With contextwindow.NextAnalytics.track({ event: 'video_completed', video_id: 'intro-video', video_title: 'Product Introduction', video_duration: 120, video_category: 'onboarding', user_watched_percent: 100, completion_time_seconds: 118, playback_quality: '720p'});
// Avoid - Minimal contextwindow.NextAnalytics.track({ event: 'video_completed' // Missing critical context});4. Track User Journey Flows
Section titled “4. Track User Journey Flows”For multi-step flows, track key checkpoints using consistent flow IDs:
// Generate a unique flow IDconst flowId = generateUUID();
// Track flow startwindow.NextAnalytics.track({ event: 'checkout_flow_started', flow_id: flowId, entry_point: 'cart_page', cart_value: cartTotal, item_count: cartItems.length});
// Track each stepwindow.NextAnalytics.track({ event: 'checkout_step_completed', flow_id: flowId, step: 'shipping_info', step_number: 1, duration_ms: Date.now() - stepStartTime});
// Track completion or abandonmentwindow.NextAnalytics.track({ event: 'checkout_flow_completed', flow_id: flowId, steps_completed: 4, total_duration_ms: Date.now() - flowStartTime, conversion: true});5. Track Errors and Edge Cases
Section titled “5. Track Errors and Edge Cases”Capture error events for debugging and monitoring:
// Track checkout errorswindow.NextAnalytics.track({ event: 'checkout_error', error_code: 'PAYMENT_DECLINED', error_message: 'Your card was declined', step: 'payment_processing', attempted_amount: 99.99});
// Track form validation failureswindow.NextAnalytics.track({ event: 'form_validation_error', form_id: 'contact_form', field_errors: ['email', 'phone'], error_count: 2});
// Track API failureswindow.NextAnalytics.track({ event: 'api_call_failed', endpoint: '/api/orders', status_code: 500, retry_attempt: 1, error_type: 'server_error'});Error Handling
Section titled “Error Handling”Wrap Tracking in Try-Catch Blocks
Section titled “Wrap Tracking in Try-Catch Blocks”Never let analytics errors break your application functionality:
function trackAnalyticsEvent(eventName, eventData) { try { window.NextAnalytics.track({ event: eventName, ...eventData }); } catch (error) { // Log the error for monitoring console.error('Analytics tracking failed:', { event: eventName, error: error.message, stack: error.stack });
// Don't re-throw - continue with app functionality // Consider sending to error tracking service if (window.errorReporter) { window.errorReporter.captureException(error); } }}
// UsagetrackAnalyticsEvent('form_submitted', { form_id: 'contact', fields_count: 5});Implement Graceful Degradation
Section titled “Implement Graceful Degradation”Ensure the app works even if analytics fails:
// Wrap the entire initializationtry { window.nextConfig = { apiKey: 'your-api-key', analytics: { enabled: true } };} catch (error) { console.warn('Analytics initialization failed:', error); // App continues to work without analytics}
// For critical tracking, use a wrapperasync function trackPurchaseWithFallback(orderData) { try { await window.NextAnalytics.trackAsync({ event: 'purchase', ...orderData }); } catch (error) { console.error('Failed to track purchase:', error);
// Fallback: Send to error service if (window.errorReporter) { window.errorReporter.captureException(error, { tags: { event: 'purchase_tracking_failed' }, extra: { orderData } }); } }}Performance Optimization
Section titled “Performance Optimization”1. Batch Events
Section titled “1. Batch Events”For multiple events in quick succession, batch them together:
// Avoid - Multiple individual callsitems.forEach(item => { window.NextAnalytics.track({ event: 'item_viewed', item_id: item.id });});
// Better - Batch in a single eventwindow.NextAnalytics.track({ event: 'items_batch_viewed', items: items.map(item => ({ item_id: item.id, item_title: item.title })), batch_size: items.length, timestamp: Date.now()});2. Lazy Load Analytics
Section titled “2. Lazy Load Analytics”For non-critical tracking, defer analytics initialization:
// Defer analytics to idle timeif ('requestIdleCallback' in window) { requestIdleCallback(() => { initializeAnalytics(); });} else { // Fallback for browsers without requestIdleCallback setTimeout(initializeAnalytics, 2000);}
function initializeAnalytics() { window.nextConfig = { apiKey: 'your-api-key', analytics: { enabled: true } };}3. Use Appropriate API Based on Timing
Section titled “3. Use Appropriate API Based on Timing”Choose the API that matches your timing needs:
// Immediate tracking (synchronous)next.trackAddToCart('item-123', 1);
// For critical events that must completeawait window.NextAnalytics.trackAsync({ event: 'purchase_confirmation', order_id: 'ORD-12345'});
// For background trackingwindow.NextAnalytics.track({ event: 'page_interaction', interaction_type: 'scroll' // Fires in background, doesn't block});4. Debounce Frequent Events
Section titled “4. Debounce Frequent Events”For high-frequency events, debounce to reduce overhead:
// Debounce scroll trackinglet scrollTimeout;window.addEventListener('scroll', () => { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(() => { window.NextAnalytics.track({ event: 'page_scrolled', scroll_depth: (window.scrollY / document.body.scrollHeight) * 100 }); }, 1000); // Track once per second max});
// Debounce resize eventslet resizeTimeout;window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { window.NextAnalytics.track({ event: 'viewport_changed', width: window.innerWidth, height: window.innerHeight }); }, 500);});Custom Events Best Practices
Section titled “Custom Events Best Practices”1. Use EventBuilder for E-commerce Events
Section titled “1. Use EventBuilder for E-commerce Events”Use EventBuilder for e-commerce events to ensure GA4 compliance:
import { EventBuilder, dataLayer } from '@/utils/analytics';
// Track wishlist additionconst item = { id: 'pkg-123', title: 'Premium Package', price: 99.99, category: 'packages'};
const event = EventBuilder.createEvent('wishlist_add', { ecommerce: { currency: EventBuilder.getCurrency(), items: [EventBuilder.formatEcommerceItem(item, 0)] }});
dataLayer.push(event);2. Use Transform Functions for Data Enrichment
Section titled “2. Use Transform Functions for Data Enrichment”Enrich events globally without modifying every tracking call:
import { dataLayer } from '@/utils/analytics';
// Add context to all eventsdataLayer.setTransformFunction((event) => { // Add app metadata event.app_version = window.APP_VERSION; event.environment = window.ENV;
// Add user context if (window.currentUser) { event.user_id = window.currentUser.id; event.user_tier = window.currentUser.tier; }
// Add session data event.session_duration = Date.now() - window.sessionStartTime;
return event;});3. Filter Events Strategically
Section titled “3. Filter Events Strategically”Use transform functions to filter out unwanted events:
dataLayer.setTransformFunction((event) => { // Filter out internal events in production const internalEvents = ['internal_test', 'dev_event']; if (internalEvents.includes(event.event) && window.ENV === 'production') { return null; // Event won't be sent }
// Filter test users if (event.user_properties?.customer_email?.includes('@test.com')) { return null; }
return event;});4. Validate Events in Development
Section titled “4. Validate Events in Development”Enable event validation to catch issues early:
if (process.env.NODE_ENV === 'development') { import { EventValidator } from '@/utils/analytics';
const validator = new EventValidator(true);
// Create a wrapper function const trackWithValidation = (eventName, eventData) => { const event = { event: eventName, ...eventData };
// Validate before tracking const result = validator.validateEvent(event); if (!result.valid) { console.error('Invalid event:', { event: eventName, errors: result.errors, data: eventData }); return; // Don't send invalid events }
window.NextAnalytics.track(event); };
// Use throughout app window.trackWithValidation = trackWithValidation;}Provider-Specific Best Practices
Section titled “Provider-Specific Best Practices”Google Tag Manager
Section titled “Google Tag Manager”window.nextConfig = { analytics: { providers: { gtm: { enabled: true // Optional: custom settings } } }};- Test in GTM Preview Mode: Always test events in GTM’s preview mode before publishing containers.
// Verify GTM is loadingconsole.log('GTM ready:', typeof dataLayer !== 'undefined');
// Check event in Previewwindow.NextAnalytics.track({ event: 'test_event', timestamp: new Date().toISOString()});-
Use GTM’s Debug View: Enable GTM’s Real-time Debug View in Google Analytics for verification.
-
Version Control Containers: Use GTM’s version history feature to manage changes.
Facebook Pixel
Section titled “Facebook Pixel”window.nextConfig = { storeName: 'my-store', // Critical for deduplication analytics: { providers: { facebook: { enabled: true, settings: { pixelId: 'YOUR_PIXEL_ID' } } } }};- Set storeName: Required for purchase deduplication across web and app.
window.nextConfig = { storeName: 'consistent-store-name', // Use same name in app SDK analytics: { ... }};- Track Purchase Immediately: Track purchases on confirmation page before navigation.
window.addEventListener('DOMContentLoaded', () => { next.trackPurchase(orderData);});- Use Correct Currency: Ensure currency matches your Facebook Ads account settings.
window.nextConfig = { currency: 'USD', // Match your ad account currency analytics: { ... }};- Verify in Pixel Helper: Use Facebook’s Pixel Helper browser extension to verify events.
RudderStack
Section titled “RudderStack”window.nextConfig = { analytics: { providers: { rudderstack: { enabled: true, settings: { writeKey: 'YOUR_WRITE_KEY', dataPlaneURL: 'https://your-dataplane.rudderstack.com' } } } }};- Identify Users: Set user context for better tracking.
// After loginwindow.NextAnalytics.setUserProperties({ user_id: user.id, email: user.email, name: user.name, plan: user.plan});- Use Traits: Attach user traits for segmentation.
window.NextAnalytics.track({ event: 'feature_used', feature: 'advanced_search', traits: { plan: 'premium', company: 'acme-corp' }});- Monitor Transformations: Use RudderStack’s transformations for event routing.
Custom Webhooks
Section titled “Custom Webhooks”window.nextConfig = { analytics: { providers: { custom: { enabled: true, settings: { endpoint: 'https://api.yourapp.com/events', batchSize: 10, flushInterval: 5000 } } } }};- Implement Retry Logic: Handle failed requests gracefully.
// Server-side webhook handlerapp.post('/events', async (req, res) => { const events = req.body.events;
try { await processEvents(events); res.json({ success: true, processedCount: events.length }); } catch (error) { // Client will retry res.status(500).json({ error: 'Processing failed' }); }});- Validate Signatures: Verify webhook authenticity if sensitive.
// Client-side: Add signature to webhookconst crypto = require('crypto');const signature = crypto .createHmac('sha256', SECRET_KEY) .update(JSON.stringify(events)) .digest('hex');
// Include in request headers- Handle Batch Events: Custom webhooks receive batched events for efficiency.
// Webhook receives multiple events{ "events": [ { "event": "view_item", ... }, { "event": "add_to_cart", ... }, { "event": "begin_checkout", ... } ]}Testing Strategies
Section titled “Testing Strategies”1. Development Testing Checklist
Section titled “1. Development Testing Checklist”Use this checklist when testing analytics implementation:
// 1. Verify initializationconsole.assert( typeof window.NextAnalytics !== 'undefined', 'NextAnalytics not initialized');
// 2. Check debug modewindow.NextAnalytics.setDebugMode(true);
// 3. Test simple eventwindow.NextAnalytics.track({ event: 'test_event', timestamp: new Date().toISOString()});
// 4. Test add to cartnext.trackAddToCart('test-item', 1);
// 5. Test begin checkoutnext.trackBeginCheckout();
// 6. Verify in consoleconst status = window.NextAnalytics.getStatus();console.log('Analytics status:', status);2. Browser Console Testing
Section titled “2. Browser Console Testing”Monitor events in real-time using the browser console:
// Enable debug modewindow.NextAnalytics.setDebugMode(true);
// Monitor all eventswindow.addEventListener('NextAnalyticsEvent', (event) => { console.log('Event tracked:', event.detail);});
// Check data layerconsole.log('Data layer:', window.NextDataLayer);
// Verify provider routingconst status = window.NextAnalytics.getStatus();console.table(status.providers);3. Testing Purchase Flow
Section titled “3. Testing Purchase Flow”Test the complete purchase flow:
// Simulate checkout initiationwindow.addEventListener('DOMContentLoaded', () => { // Step 1: View items next.trackViewItem('item-1'); next.trackViewItem('item-2');
// Step 2: Add to cart next.trackAddToCart('item-1', 1); next.trackAddToCart('item-2', 2);
// Step 3: Begin checkout next.trackBeginCheckout();
// Step 4: Track purchase next.trackPurchase({ id: 'ORDER_' + Date.now(), total: 199.99, currency: 'USD', items: [ { id: 'item-1', title: 'Item 1', price: 99.99 }, { id: 'item-2', title: 'Item 2', price: 100.00 } ] });});4. Error Scenario Testing
Section titled “4. Error Scenario Testing”Test error handling:
// Test error trackingwindow.NextAnalytics.track({ event: 'payment_error', error_code: 'CARD_DECLINED', error_message: 'Your card was declined', attempted_amount: 99.99});
// Test recoverywindow.NextAnalytics.track({ event: 'payment_retry', retry_attempt: 2, original_error: 'CARD_DECLINED'});
// Verify error is tracked but doesn't break appconsole.assert( document.body !== null, 'App broken after tracking error');Production Checklist
Section titled “Production Checklist”Before deploying analytics to production, verify:
- Analytics enabled in production config
- Debug mode disabled (set based on environment)
- storeName set for Facebook Pixel
- All required API keys configured via environment variables
- Mode set to ‘auto’ (unless specific reason for manual)
- All required providers enabled
- Custom webhook endpoints verified if using custom provider
- All purchase tracking implemented on confirmation page
- All custom events use consistent snake_case naming
- Error handling wrapped around all tracking calls
- Critical events (purchase, signup) have error handling
- Transform functions added for data enrichment
- Event validation enabled in development
- No PII tracked (emails, phone numbers) in non-critical events
- Tested with GTM Debug View (if using GTM)
- Tested with Facebook Pixel Helper (if using Facebook)
- Verified purchase events on test transaction
- Verified custom events in GA4 Real-time Report
- Tested error scenarios (failed payment, form errors)
- Verified browser console has no analytics errors
- Cross-browser testing completed
- Mobile testing completed
- Set up alerting for tracking failures
- Monitor event delivery rates
- Check for unexpected event spikes
- Review GA4 real-time dashboard for first 24 hours
- Verify revenue data matches internal records
- Monitor error event rates
- Set up weekly data quality checks
- Document all custom events and their parameters
- Document any event transform functions
- Document custom webhook endpoint schema
- Document any environment-specific configuration
- Create runbook for disabling analytics if needed
- Document provider credentials location
- Keep change log of analytics modifications
Common Patterns and Examples
Section titled “Common Patterns and Examples”User Journey Tracking
Section titled “User Journey Tracking”Track complete user flows with contextual data:
class UserJourneyTracker { constructor() { this.journeyId = this.generateId(); this.events = []; this.startTime = Date.now(); }
generateId() { return `journey_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }
trackStep(stepName, stepData = {}) { const eventData = { event: `journey_${stepName}`, journey_id: this.journeyId, step_timestamp: Date.now(), elapsed_time: Date.now() - this.startTime, ...stepData };
window.NextAnalytics.track(eventData); this.events.push(eventData); }
complete(metadata = {}) { this.trackStep('completed', { total_duration: Date.now() - this.startTime, total_events: this.events.length, ...metadata }); }
abandon(reason, metadata = {}) { this.trackStep('abandoned', { abandon_reason: reason, last_step: this.events[this.events.length - 1]?.event, ...metadata }); }}
// Usageconst journey = new UserJourneyTracker();journey.trackStep('initiated', { entry_point: 'homepage' });journey.trackStep('browsing', { category_viewed: 'premium-packages' });journey.trackStep('added_to_cart', { product_id: 'pkg-123' });journey.complete({ conversion: true });Feature Flag Analytics
Section titled “Feature Flag Analytics”Track feature flag usage:
function trackFeatureUsage(featureName, enabled, metadata = {}) { window.NextAnalytics.track({ event: 'feature_flag_evaluated', feature_name: featureName, enabled: enabled, timestamp: Date.now(), ...metadata });}
// Usageif (shouldEnableNewCheckout()) { trackFeatureUsage('new_checkout_ui', true, { variant: 'experimental', user_segment: 'premium' }); renderNewCheckout();} else { trackFeatureUsage('new_checkout_ui', false, { reason: 'user_segment_not_matched' }); renderLegacyCheckout();}Conversion Funnel Tracking
Section titled “Conversion Funnel Tracking”Track conversion funnel steps:
class FunnelTracker { constructor(funnelName) { this.funnelName = funnelName; this.funnelId = `funnel_${Date.now()}`; this.steps = []; }
trackStep(stepName, stepNumber, metadata = {}) { const event = { event: `funnel_step`, funnel_name: this.funnelName, funnel_id: this.funnelId, step_name: stepName, step_number: stepNumber, steps_completed: stepNumber, ...metadata };
window.NextAnalytics.track(event); this.steps.push(event); }
trackDropoff(stepName, stepNumber, reason, metadata = {}) { window.NextAnalytics.track({ event: `funnel_dropoff`, funnel_name: this.funnelName, funnel_id: this.funnelId, dropped_at_step: stepName, step_number: stepNumber, dropoff_reason: reason, steps_completed: stepNumber - 1, ...metadata }); }}
// Usageconst checkoutFunnel = new FunnelTracker('checkout');checkoutFunnel.trackStep('shipping', 1, { state_entered: 'CA' });checkoutFunnel.trackStep('payment', 2, { payment_method: 'credit_card' });checkoutFunnel.trackDropoff('confirmation', 3, 'payment_failed', { error_code: 'CARD_DECLINED'});Summary
Section titled “Summary”Following these practices:
- Events reach their destinations consistently
- Events contain context for analysis
- Analytics doesn’t slow down your application
- Consistent patterns make code easier to understand
- Error handling and configuration provide stable systems
Review this guide regularly and update your implementation as needed.