Custom Events & Advanced Tracking
Track custom business events and transform events before they’re sent to providers.
Simple Custom Events
Section titled “Simple Custom Events”Track custom events with arbitrary data:
window.NextAnalytics.track({ event: 'newsletter_subscribe', list_name: 'Weekly Newsletter', source: 'footer_form'});Custom events are sent to all enabled providers and stored in window.NextDataLayer.
Automatic Enrichment
Section titled “Automatic Enrichment”Every event is automatically enriched with:
- event_id - Unique event identifier
- event_time - ISO timestamp
- user_properties - Current user data (visitor_type, customer_email, etc.)
- attribution - UTM parameters, funnel, affiliate data
- session_id - Current session identifier
- Context - page_location, page_title, user_agent, screen_resolution, viewport_size
You don’t need to add these manually - they’re included automatically.
Common Custom Event Examples
Section titled “Common Custom Event Examples”// Newsletter subscriptionwindow.NextAnalytics.track({ event: 'newsletter_subscribe', list: 'weekly_digest', source: 'footer'});// Video engagementwindow.NextAnalytics.track({ event: 'video_played', video_id: 'intro-demo', video_title: 'Product Introduction', duration: 120});
window.NextAnalytics.track({ event: 'video_completed', video_id: 'intro-demo', percent_watched: 100});// Form submissionwindow.NextAnalytics.track({ event: 'form_submitted', form_id: 'contact', form_type: 'contact_us', fields_filled: 5});// Feature usagewindow.NextAnalytics.track({ event: 'feature_used', feature_name: 'product_comparison', items_compared: 3, user_id: currentUserId});E-commerce Custom Events
Section titled “E-commerce Custom Events”For custom e-commerce events, include product data using the GA4 ecommerce format:
window.NextAnalytics.track({ event: 'wishlist_add', ecommerce: { currency: 'USD', value: 99.99, items: [{ item_id: 'SKU-123', item_name: 'Product Name', item_category: 'Electronics', price: 99.99, quantity: 1 }] }});Custom E-commerce Examples
Section titled “Custom E-commerce Examples”function trackWishlistAdd(item) { window.NextAnalytics.track({ event: 'wishlist_add', ecommerce: { currency: 'USD', value: item.price, items: [{ item_id: item.sku, item_name: item.name, item_category: item.category, price: item.price, quantity: 1 }] } });}function trackProductComparison(products) { window.NextAnalytics.track({ event: 'product_compare', comparison_count: products.length, ecommerce: { currency: 'USD', items: products.map((product, index) => ({ item_id: product.sku, item_name: product.name, item_category: product.category, price: product.price, quantity: 1, index: index })) } });}function trackQuickView(item) { window.NextAnalytics.track({ event: 'quick_view', view_type: 'modal', ecommerce: { currency: 'USD', value: item.price, items: [{ item_id: item.sku, item_name: item.name, item_category: item.category, price: item.price, quantity: 1 }] } });}Transform Functions
Section titled “Transform Functions”Modify ALL events before they’re sent to providers using transform functions.
Using DataLayerManager
Section titled “Using DataLayerManager”// Access the data layer managerconst dataLayer = window.NextDataLayerManager;
dataLayer.setTransformFunction((event) => { // Add custom fields to every event event.app_version = '1.0.0'; event.environment = 'production'; event.custom_user_id = getCurrentUserId();
// Filter out events if (event.event === 'internal_test') { return null; // Event won't be sent }
// Modify specific events if (event.event === 'dl_purchase') { event.ecommerce.custom_order_type = getOrderType(); event.fulfillment_center = 'US-WEST'; }
return event;});Global Transform Function
Section titled “Global Transform Function”You can also set a transform function globally in the window:
window.NextDataLayerTransformFn = function(event) { event.custom_property = 'value'; return event;};This runs before the configured transform function.
Transform Function Use Cases
Section titled “Transform Function Use Cases”window.NextDataLayerTransformFn = function(event) { // Add app context to all events event.app_version = window.APP_VERSION; event.environment = window.ENV; event.build_number = window.BUILD_NUM;
// Add user context if (window.userSession) { event.session_duration = Date.now() - window.userSession.startTime; event.page_views = window.userSession.pageViews; }
return event;};window.NextDataLayerTransformFn = function(event) { // Filter out internal events const internalEvents = ['internal_test', 'dev_event', 'debug']; if (internalEvents.includes(event.event)) { return null; // Don't send }
// Filter test users if (event.user_properties?.customer_email?.includes('@test.com')) { return null; }
return event;};window.NextDataLayerTransformFn = function(event) { // Remove PII in certain environments if (window.ENV === 'development') { if (event.user_properties) { event.user_properties.customer_phone = 'REDACTED'; delete event.user_properties.customer_address_1; } }
return event;};window.NextDataLayerTransformFn = function(event) { // Add region-specific data const region = getUserRegion(); event.region = region; event.currency_override = getRegionalCurrency(region);
// Route high-value purchases if (event.event === 'dl_purchase' && event.ecommerce.value > 1000) { event.priority = 'high'; event.fraud_check_required = true; }
return event;};Advanced Event Patterns
Section titled “Advanced Event Patterns”Event Chaining
Section titled “Event Chaining”Track sequences of related events:
// Start checkout flowwindow.NextAnalytics.track({ event: 'checkout_flow_started', flow_id: generateFlowId(), entry_point: 'cart_page'});
// Track each stepwindow.NextAnalytics.track({ event: 'checkout_step_completed', flow_id: currentFlowId, step: 'shipping_info', duration_ms: stepDuration});
// Track completionwindow.NextAnalytics.track({ event: 'checkout_flow_completed', flow_id: currentFlowId, total_duration_ms: totalDuration, steps_completed: 4});Conditional Event Tracking
Section titled “Conditional Event Tracking”Track events based on business logic:
function trackCartMilestone(cartValue) { const milestones = [ { threshold: 50, name: 'free_shipping_eligible' }, { threshold: 100, name: 'discount_eligible' }, { threshold: 200, name: 'premium_tier_reached' } ];
milestones.forEach(milestone => { if (cartValue >= milestone.threshold && !hasMilestone(milestone.name)) { window.NextAnalytics.track({ event: 'cart_milestone', milestone: milestone.name, cart_value: cartValue, threshold: milestone.threshold });
saveMilestone(milestone.name); } });}Time-based Event Tracking
Section titled “Time-based Event Tracking”Track engagement duration:
class VideoTracker { constructor(videoId) { this.videoId = videoId; this.startTime = Date.now(); this.milestones = [25, 50, 75, 100]; this.tracked = new Set(); }
trackProgress(percentComplete) { this.milestones.forEach(milestone => { if (percentComplete >= milestone && !this.tracked.has(milestone)) { window.NextAnalytics.track({ event: 'video_progress', video_id: this.videoId, milestone: milestone, duration_watched: Date.now() - this.startTime });
this.tracked.add(milestone); } }); }
trackComplete() { window.NextAnalytics.track({ event: 'video_completed', video_id: this.videoId, total_duration: Date.now() - this.startTime }); }}Best Practices
Section titled “Best Practices”1. Consistent Event Naming
Section titled “1. Consistent Event Naming”Use snake_case for all custom events:
// Good'newsletter_subscribe''video_completed''form_submitted''feature_enabled'
// Avoid'subscribeNewsletter''VideoCompleted''form-submitted''FEATURE_ENABLED'2. Include Context
Section titled “2. Include Context”Provide event context:
// 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});
// Avoid - Minimal contextwindow.NextAnalytics.track({ event: 'video_completed'});3. Use GA4 Ecommerce Format
Section titled “3. Use GA4 Ecommerce Format”For e-commerce events, use the GA4 standard format:
// Good - GA4 formatwindow.NextAnalytics.track({ event: 'wishlist_add', ecommerce: { currency: 'USD', value: 99.99, items: [{ item_id: 'SKU-123', item_name: 'Product Name', price: 99.99, quantity: 1 }] }});
// Avoid - Non-standard formatwindow.NextAnalytics.track({ event: 'wishlist_add', product: item // Wrong structure});4. Handle Errors Gracefully
Section titled “4. Handle Errors Gracefully”Never let analytics errors break your app:
try { window.NextAnalytics.track(customEvent);} catch (error) { console.error('Analytics tracking failed:', error); // Don't throw - continue with app functionality}5. Debug in Development
Section titled “5. Debug in Development”Enable debug mode to see detailed logs:
// In browser consolewindow.NextAnalytics.setDebugMode(true);
// Or via configwindow.nextConfig = { analytics: { debug: true }};Examples
Section titled “Examples”Product Recommendation Tracking
Section titled “Product Recommendation Tracking”function trackRecommendationClick(item, recommendationType) { window.NextAnalytics.track({ event: 'recommendation_clicked', item_id: item.id, item_name: item.title, recommendation_type: recommendationType, // 'similar', 'upsell', 'cross-sell' recommendation_position: item.position, algorithm: 'collaborative_filtering' });}A/B Test Tracking
Section titled “A/B Test Tracking”function trackExperiment(experimentId, variantId) { window.NextAnalytics.track({ event: 'experiment_viewed', experiment_id: experimentId, variant_id: variantId, user_id: getCurrentUserId() });}
function trackExperimentConversion(experimentId, variantId) { window.NextAnalytics.track({ event: 'experiment_converted', experiment_id: experimentId, variant_id: variantId, conversion_value: getCartValue() });}Search Tracking
Section titled “Search Tracking”function trackSearch(query, resultsCount) { window.NextAnalytics.track({ event: 'search_performed', search_query: query, results_count: resultsCount, search_filters: getActiveFilters(), search_sort: getCurrentSort() });}
function trackSearchResultClick(query, item, position) { window.NextAnalytics.track({ event: 'search_result_clicked', search_query: query, item_id: item.id, item_position: position, results_count: getTotalResults() });}Accessing the Data Layer
Section titled “Accessing the Data Layer”View all tracked events:
// View all eventsconsole.log(window.NextDataLayer);
// Get event countconst count = window.NextDataLayerManager.getEventCount();console.log(`Tracked ${count} events`);
// Get analytics statusconst status = window.NextAnalytics.getStatus();console.log(status);// {// initialized: true,// debugMode: false,// providers: ['GTM', 'Facebook'],// eventsTracked: 15,// ignored: false// }Related Documentation
Section titled “Related Documentation”- Analytics Overview - Main analytics documentation
- Tracking API - Core tracking methods and events
- Configuration - SDK configuration options
- Providers - Provider setup guides