Next Commerce
Analytics

Best Practices

Patterns and recommendations for implementing Campaign Cart analytics including configuration strategies, event naming conventions, tracking patterns, and optimization techniques.

This guide covers patterns and recommendations for implementing Campaign Cart analytics across your application.

Configuration Best Practices

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 load
  • dl_view_item_list - Collection/category pages
  • dl_add_to_cart - Cart additions
  • dl_begin_checkout - Checkout initiation

Manual Tracking Required:

  • Purchase events (order confirmation page)
  • Login/signup events
  • Custom business events

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

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

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

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_CASE

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

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 page
window.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

For events that might cause navigation, track before the navigation occurs:

// Bad - Event might not send before navigation
document.getElementById('checkout-btn').addEventListener('click', () => {
  next.trackBeginCheckout();
  window.location.href = '/checkout';  // May interrupt tracking
});

// Good - Track with callback
document.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 tracking
document.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

Provide context data with custom events:

// Good - With context
window.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 context
window.NextAnalytics.track({
  event: 'video_completed'
  // Missing critical context
});

4. Track User Journey Flows

For multi-step flows, track key checkpoints using consistent flow IDs:

// Generate a unique flow ID
const flowId = generateUUID();

// Track flow start
window.NextAnalytics.track({
  event: 'checkout_flow_started',
  flow_id: flowId,
  entry_point: 'cart_page',
  cart_value: cartTotal,
  item_count: cartItems.length
});

// Track each step
window.NextAnalytics.track({
  event: 'checkout_step_completed',
  flow_id: flowId,
  step: 'shipping_info',
  step_number: 1,
  duration_ms: Date.now() - stepStartTime
});

// Track completion or abandonment
window.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

Capture error events for debugging and monitoring:

// Track checkout errors
window.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 failures
window.NextAnalytics.track({
  event: 'form_validation_error',
  form_id: 'contact_form',
  field_errors: ['email', 'phone'],
  error_count: 2
});

// Track API failures
window.NextAnalytics.track({
  event: 'api_call_failed',
  endpoint: '/api/orders',
  status_code: 500,
  retry_attempt: 1,
  error_type: 'server_error'
});

Error Handling

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);
    }
  }
}

// Usage
trackAnalyticsEvent('form_submitted', {
  form_id: 'contact',
  fields_count: 5
});

Implement Graceful Degradation

Ensure the app works even if analytics fails:

// Wrap the entire initialization
try {
  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 wrapper
async 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

1. Batch Events

For multiple events in quick succession, batch them together:

// Avoid - Multiple individual calls
items.forEach(item => {
  window.NextAnalytics.track({
    event: 'item_viewed',
    item_id: item.id
  });
});

// Better - Batch in a single event
window.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

For non-critical tracking, defer analytics initialization:

// Defer analytics to idle time
if ('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

Choose the API that matches your timing needs:

// Immediate tracking (synchronous)
next.trackAddToCart('item-123', 1);

// For critical events that must complete
await window.NextAnalytics.trackAsync({
  event: 'purchase_confirmation',
  order_id: 'ORD-12345'
});

// For background tracking
window.NextAnalytics.track({
  event: 'page_interaction',
  interaction_type: 'scroll'
  // Fires in background, doesn't block
});

4. Debounce Frequent Events

For high-frequency events, debounce to reduce overhead:

// Debounce scroll tracking
let 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 events
let 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

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 addition
const 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

Enrich events globally without modifying every tracking call:

import { dataLayer } from '@/utils/analytics';

// Add context to all events
dataLayer.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

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

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

Google Tag Manager

window.nextConfig = {
  analytics: {
    providers: {
      gtm: {
        enabled: true
        // Optional: custom settings
      }
    }
  }
};
  1. Test in GTM Preview Mode: Always test events in GTM's preview mode before publishing containers.
// Verify GTM is loading
console.log('GTM ready:', typeof dataLayer !== 'undefined');

// Check event in Preview
window.NextAnalytics.track({
  event: 'test_event',
  timestamp: new Date().toISOString()
});
  1. Use GTM's Debug View: Enable GTM's Real-time Debug View in Google Analytics for verification.

  2. Version Control Containers: Use GTM's version history feature to manage changes.

Facebook Pixel

window.nextConfig = {
  storeName: 'my-store',  // Critical for deduplication
  analytics: {
    providers: {
      facebook: {
        enabled: true,
        settings: {
          pixelId: 'YOUR_PIXEL_ID'
        }
      }
    }
  }
};
  1. Set storeName: Required for purchase deduplication across web and app.
window.nextConfig = {
  storeName: 'consistent-store-name',  // Use same name in app SDK
  analytics: { ... }
};
  1. Track Purchase Immediately: Track purchases on confirmation page before navigation.
window.addEventListener('DOMContentLoaded', () => {
  next.trackPurchase(orderData);
});
  1. Use Correct Currency: Ensure currency matches your Facebook Ads account settings.
window.nextConfig = {
  currency: 'USD',  // Match your ad account currency
  analytics: { ... }
};
  1. Verify in Pixel Helper: Use Facebook's Pixel Helper browser extension to verify events.

RudderStack

window.nextConfig = {
  analytics: {
    providers: {
      rudderstack: {
        enabled: true,
        settings: {
          writeKey: 'YOUR_WRITE_KEY',
          dataPlaneURL: 'https://your-dataplane.rudderstack.com'
        }
      }
    }
  }
};
  1. Identify Users: Set user context for better tracking.
// After login
window.NextAnalytics.setUserProperties({
  user_id: user.id,
  email: user.email,
  name: user.name,
  plan: user.plan
});
  1. Use Traits: Attach user traits for segmentation.
window.NextAnalytics.track({
  event: 'feature_used',
  feature: 'advanced_search',
  traits: {
    plan: 'premium',
    company: 'acme-corp'
  }
});
  1. Monitor Transformations: Use RudderStack's transformations for event routing.

Custom Webhooks

window.nextConfig = {
  analytics: {
    providers: {
      custom: {
        enabled: true,
        settings: {
          endpoint: 'https://api.yourapp.com/events',
          batchSize: 10,
          flushInterval: 5000
        }
      }
    }
  }
};
  1. Implement Retry Logic: Handle failed requests gracefully.
// Server-side webhook handler
app.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' });
  }
});
  1. Validate Signatures: Verify webhook authenticity if sensitive.
// Client-side: Add signature to webhook
const crypto = require('crypto');
const signature = crypto
  .createHmac('sha256', SECRET_KEY)
  .update(JSON.stringify(events))
  .digest('hex');

// Include in request headers
  1. 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

1. Development Testing Checklist

Use this checklist when testing analytics implementation:

// 1. Verify initialization
console.assert(
  typeof window.NextAnalytics !== 'undefined',
  'NextAnalytics not initialized'
);

// 2. Check debug mode
window.NextAnalytics.setDebugMode(true);

// 3. Test simple event
window.NextAnalytics.track({
  event: 'test_event',
  timestamp: new Date().toISOString()
});

// 4. Test add to cart
next.trackAddToCart('test-item', 1);

// 5. Test begin checkout
next.trackBeginCheckout();

// 6. Verify in console
const status = window.NextAnalytics.getStatus();
console.log('Analytics status:', status);

2. Browser Console Testing

Monitor events in real-time using the browser console:

// Enable debug mode
window.NextAnalytics.setDebugMode(true);

// Monitor all events
window.addEventListener('NextAnalyticsEvent', (event) => {
  console.log('Event tracked:', event.detail);
});

// Check data layer
console.log('Data layer:', window.NextDataLayer);

// Verify provider routing
const status = window.NextAnalytics.getStatus();
console.table(status.providers);

3. Testing Purchase Flow

Test the complete purchase flow:

// Simulate checkout initiation
window.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

Test error handling:

// Test error tracking
window.NextAnalytics.track({
  event: 'payment_error',
  error_code: 'CARD_DECLINED',
  error_message: 'Your card was declined',
  attempted_amount: 99.99
});

// Test recovery
window.NextAnalytics.track({
  event: 'payment_retry',
  retry_attempt: 2,
  original_error: 'CARD_DECLINED'
});

// Verify error is tracked but doesn't break app
console.assert(
  document.body !== null,
  'App broken after tracking error'
);

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

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
    });
  }
}

// Usage
const 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

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
  });
}

// Usage
if (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

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
    });
  }
}

// Usage
const 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

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.

On this page