Custom Events & Advanced Tracking
Create custom analytics events and implement advanced tracking patterns with transform functions.
Track custom business events and transform events before they're sent to providers.
Simple Custom Events
Track custom events with arbitrary data:
window.NextAnalytics.track({
event: 'newsletter_subscribe',
email: '[email protected]',
list_name: 'Weekly Newsletter',
source: 'footer_form'
});Custom events are sent to all enabled providers and stored in window.NextDataLayer.
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
// Video engagement
window.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 submission
window.NextAnalytics.track({
event: 'form_submitted',
form_id: 'contact',
form_type: 'contact_us',
fields_filled: 5
});// Feature usage
window.NextAnalytics.track({
event: 'feature_used',
feature_name: 'product_comparison',
items_compared: 3,
user_id: currentUserId
});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
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
Modify ALL events before they're sent to providers using transform functions.
Using DataLayerManager
// Access the data layer manager
const 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
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
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_email = '[email protected]';
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
Event Chaining
Track sequences of related events:
// Start checkout flow
window.NextAnalytics.track({
event: 'checkout_flow_started',
flow_id: generateFlowId(),
entry_point: 'cart_page'
});
// Track each step
window.NextAnalytics.track({
event: 'checkout_step_completed',
flow_id: currentFlowId,
step: 'shipping_info',
duration_ms: stepDuration
});
// Track completion
window.NextAnalytics.track({
event: 'checkout_flow_completed',
flow_id: currentFlowId,
total_duration_ms: totalDuration,
steps_completed: 4
});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
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
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
Provide event context:
// 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
});
// Avoid - Minimal context
window.NextAnalytics.track({
event: 'video_completed'
});3. Use GA4 Ecommerce Format
For e-commerce events, use the GA4 standard format:
// Good - GA4 format
window.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 format
window.NextAnalytics.track({
event: 'wishlist_add',
product: item // Wrong structure
});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
Enable debug mode to see detailed logs:
// In browser console
window.NextAnalytics.setDebugMode(true);
// Or via config
window.nextConfig = {
analytics: {
debug: true
}
};Examples
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
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
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
View all tracked events:
// View all events
console.log(window.NextDataLayer);
// Get event count
const count = window.NextDataLayerManager.getEventCount();
console.log(`Tracked ${count} events`);
// Get analytics status
const status = window.NextAnalytics.getStatus();
console.log(status);
// {
// initialized: true,
// debugMode: false,
// providers: ['GTM', 'Facebook'],
// eventsTracked: 15,
// ignored: false
// }Related Documentation
- Analytics Overview - Main analytics documentation
- Tracking API - Core tracking methods and events
- Configuration - SDK configuration options
- Examples - Provider setup guides