Web Analytics Patterns
GA4 Event Tracking
// gtag helper wrapper
function track(eventName, params = {}) {
if (typeof gtag === 'undefined') return;
gtag('event', eventName, params);
}
// Page view (SPA)
track('page_view', {
page_title: document.title,
page_location: window.location.href,
});
// E-commerce events
track('view_item', {
currency: 'USD',
value: 29.99,
items: [{ item_id: 'SKU_123', item_name: 'Product', price: 29.99, quantity: 1 }]
});
track('add_to_cart', { currency: 'USD', value: 29.99, items: [...] });
track('purchase', {
transaction_id: 'T_12345',
value: 59.98,
currency: 'USD',
items: [...]
});
Custom Dimensions & Metrics
// GA4: set user properties
gtag('set', 'user_properties', {
subscription_tier: 'pro',
account_age_days: 120,
});
// Custom event with custom params
track('file_download', {
file_name: 'report.pdf',
file_extension: 'pdf',
link_url: '/downloads/report.pdf',
});
React Analytics Hook
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export function usePageTracking() {
const location = useLocation();
useEffect(() => {
track('page_view', {
page_path: location.pathname + location.search,
page_title: document.title,
});
}, [location]);
}
// Track button clicks declaratively
export function TrackedButton({ eventName, eventParams, ...props }) {
return (
<button
{...props}
=> {
track(eventName, eventParams);
props.onClick?.(e);
}}
/>
);
}
Privacy-First Analytics (Plausible)
<!-- No cookies, GDPR compliant -->
<script defer data-domain="example.com"
src="https://plausible.io/js/script.js"></script>
// Custom events with Plausible
window.plausible?.('Signup', { props: { plan: 'pro', source: 'landing' } });
window.plausible?.('Download', { props: { file: 'whitepaper.pdf' } });
Consent Management
// Only load analytics after consent
class ConsentManager {
#hasConsent = false;
#queue = [];
grant() {
this.#hasConsent = true;
this.#loadGA4();
this.#queue.forEach(([name, params]) => this.track(name, params));
this.#queue = [];
localStorage.setItem('analytics-consent', 'granted');
}
track(name, params) {
if (this.#hasConsent) {
gtag('event', name, params);
} else {
this.#queue.push([name, params]);
}
}
#loadGA4() {
const script = document.createElement('script');
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX';
script.async = true;
document.head.append(script);
window.dataLayer = window.dataLayer || [];
window.gtag = function() { dataLayer.push(arguments); };
gtag('js', new Date());
gtag('config', 'G-XXXXXXXX', { anonymize_ip: true });
}
}
export const analytics = new ConsentManager();
Core Web Vitals Reporting
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, rating, id }) {
track('web_vitals', {
metric_name: name,
metric_value: Math.round(value),
metric_rating: rating, // 'good' | 'needs-improvement' | 'poor'
metric_id: id,
});
}
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
UTM Parameter Handling
// Persist UTM params through the funnel
const params = new URLSearchParams(location.search);
const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
const utms = Object.fromEntries(utmKeys.filter(k => params.has(k)).map(k => [k, params.get(k)]));
if (Object.keys(utms).length) {
sessionStorage.setItem('utms', JSON.stringify(utms));
}
// Read back on conversion
const savedUtms = JSON.parse(sessionStorage.getItem('utms') || '{}');
track('purchase', { ...purchaseData, ...savedUtms });