Event Tracking in Simple Analytics
Simple Analytics provides straightforward event tracking with its privacy-first approach. Events let you track user interactions beyond page views, such as button clicks, form submissions, purchases, and custom actions - all without compromising visitor privacy or requiring cookies.
Event Tracking Philosophy
Simple Analytics' event tracking is designed to be:
- Simple: Single function call to track events
- Privacy-Friendly: No user identification or cookies
- Lightweight: Minimal JavaScript overhead
- Flexible: Track any custom action
- Aggregate-Only: Events are counted, not tied to individual users
Basic Event Tracking
JavaScript API:
sa_event('event_name');
Example Events:
// Newsletter signup
sa_event('newsletter_signup');
// Button click
sa_event('cta_click');
// Download
sa_event('file_download');
Event with Metadata
Simple Analytics supports event metadata for additional context:
sa_event('event_name', { key: 'value' });
E-Commerce Example:
// Purchase event
sa_event('purchase', {
amount: 99.99,
currency: 'USD',
product: 'Pro Plan'
});
Feature Usage:
sa_event('feature_used', {
feature: 'export_data',
format: 'CSV'
});
Common Event Tracking Patterns
User Interactions
Button Clicks:
// Track specific button
document.getElementById('signup-button').addEventListener('click', () => {
sa_event('button_click', {
button: 'signup',
location: 'hero'
});
});
// Track all CTA buttons
document.querySelectorAll('.cta-button').forEach(button => {
button.addEventListener('click', (e) => {
sa_event('cta_click', {
text: e.target.textContent.trim(),
url: e.target.href
});
});
});
Link Clicks:
// Track outbound links
document.querySelectorAll('a[href^="http"]').forEach(link => {
link.addEventListener('click', (e) => {
const url = e.target.href;
if (!url.includes(window.location.hostname)) {
sa_event('outbound_link', {
destination: url
});
}
});
});
Navigation Menu Clicks:
document.querySelectorAll('.main-nav a').forEach(link => {
link.addEventListener('click', (e) => {
sa_event('nav_click', {
section: e.target.dataset.section,
label: e.target.textContent.trim()
});
});
});
Form Tracking
Form Submission:
document.getElementById('contact-form').addEventListener('submit', (e) => {
sa_event('form_submit', {
form: 'contact',
method: e.target.method
});
});
Newsletter Signup:
document.getElementById('newsletter-form').addEventListener('submit', async (e) => {
e.preventDefault();
// Track event
sa_event('newsletter_signup', {
source: 'footer'
});
// Submit form
await submitForm(e.target);
});
Form Errors:
function validateForm(form) {
const errors = [];
if (!form.email.value.includes('@')) {
errors.push('email');
sa_event('form_error', {
form: 'signup',
field: 'email',
error: 'invalid_format'
});
}
return errors;
}
E-Commerce Events
Product Views:
// Track when user views product page
sa_event('product_view', {
product_id: product.id,
product_name: product.name,
category: product.category,
price: product.price
});
Add to Cart:
function addToCart(product, quantity) {
sa_event('add_to_cart', {
product: product.name,
quantity: quantity,
value: product.price * quantity
});
// Add to cart logic...
}
Checkout Funnel:
// Step 1: Checkout initiated
sa_event('checkout_started', {
cart_value: getCartTotal(),
items: getCartItems().length
});
// Step 2: Shipping info entered
sa_event('shipping_info_entered');
// Step 3: Payment method selected
sa_event('payment_method_selected', {
method: 'credit_card'
});
// Step 4: Order completed
sa_event('purchase_completed', {
order_id: orderId,
value: orderTotal,
currency: 'USD'
});
Cart Abandonment:
// Track when user leaves checkout without completing
window.addEventListener('beforeunload', () => {
if (isOnCheckoutPage() && !orderCompleted) {
sa_event('cart_abandoned', {
cart_value: getCartTotal(),
items_count: getCartItems().length
});
}
});
Content Engagement
Video Tracking:
const video = document.getElementById('promo-video');
// Video play
video.addEventListener('play', () => {
sa_event('video_play', {
video_id: 'product_demo',
duration: video.duration
});
});
// Video milestone tracking (25%, 50%, 75%, 100%)
const milestones = [25, 50, 75, 100];
const trackedMilestones = new Set();
video.addEventListener('timeupdate', () => {
const percent = (video.currentTime / video.duration) * 100;
milestones.forEach(milestone => {
if (percent >= milestone && !trackedMilestones.has(milestone)) {
trackedMilestones.add(milestone);
sa_event('video_progress', {
video_id: 'product_demo',
milestone: milestone
});
}
});
});
// Video completed
video.addEventListener('ended', () => {
sa_event('video_complete', {
video_id: 'product_demo'
});
});
Scroll Depth:
const scrollDepths = [25, 50, 75, 100];
const trackedDepths = new Set();
window.addEventListener('scroll', () => {
const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
scrollDepths.forEach(depth => {
if (scrollPercent >= depth && !trackedDepths.has(depth)) {
trackedDepths.add(depth);
sa_event('scroll_depth', {
depth: depth,
page: window.location.pathname
});
}
});
});
File Downloads:
document.querySelectorAll('a[download]').forEach(link => {
link.addEventListener('click', (e) => {
const fileName = e.target.getAttribute('download') || e.target.href.split('/').pop();
const fileExt = fileName.split('.').pop();
sa_event('file_download', {
file: fileName,
type: fileExt
});
});
});
Search Queries:
document.getElementById('search-form').addEventListener('submit', (e) => {
e.preventDefault();
const query = e.target.querySelector('input[name="q"]').value;
sa_event('search', {
query: query.toLowerCase(),
results: getSearchResultsCount()
});
performSearch(query);
});
SaaS Application Events
Feature Usage:
// Track when user uses a specific feature
sa_event('export_data', {
format: 'CSV',
records: recordCount
});
sa_event('report_generated', {
report_type: 'monthly_summary',
date_range: '30_days'
});
Account Events:
// Trial started
sa_event('trial_started', {
plan: 'pro',
trial_days: 14
});
// Upgrade
sa_event('plan_upgraded', {
from: 'basic',
to: 'pro',
billing: 'monthly'
});
// Cancellation
sa_event('subscription_cancelled', {
plan: 'pro',
reason: 'cost'
});
Onboarding Steps:
// Track onboarding progress
sa_event('onboarding_step_completed', {
step: 'profile_setup',
step_number: 1,
total_steps: 5
});
Framework-Specific Implementation
React
import { useCallback } from 'react';
export function useAnalytics() {
const trackEvent = useCallback((eventName, eventData) => {
if (typeof window !== 'undefined' && window.sa_event) {
window.sa_event(eventName, eventData);
}
}, []);
return { trackEvent };
}
// Usage in component
function SignupButton() {
const { trackEvent } = useAnalytics();
const handleClick = () => {
trackEvent('signup_button_click', {
location: 'header'
});
};
return <button Up</button>;
}
Vue.js
// Global mixin
export default {
methods: {
$trackEvent(eventName, eventData) {
if (window.sa_event) {
window.sa_event(eventName, eventData);
}
}
}
};
// In component
<template>
<button @click="handleClick">Buy Now</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$trackEvent('purchase_initiated', {
product: 'Pro Plan',
price: 99
});
}
}
};
</script>
Next.js
// utils/analytics.js
export const trackEvent = (eventName, eventData) => {
if (typeof window !== 'undefined' && window.sa_event) {
window.sa_event(eventName, eventData);
}
};
// In component
import { trackEvent } from '@/utils/analytics';
export default function ProductCard({ product }) {
const handleAddToCart = () => {
trackEvent('add_to_cart', {
product_id: product.id,
price: product.price
});
// Add to cart logic...
};
return (
<button to Cart</button>
);
}
Helper Functions
Safe Event Tracking:
function trackEvent(eventName, eventData = {}) {
try {
if (typeof window !== 'undefined' && window.sa_event) {
window.sa_event(eventName, eventData);
} else if (process.env.NODE_ENV === 'development') {
console.log('[SA Event]', eventName, eventData);
}
} catch (error) {
console.error('Error tracking event:', error);
}
}
Debounced Event Tracking:
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const trackScrollDebounced = debounce(() => {
sa_event('page_scrolled', {
scroll_position: window.scrollY
});
}, 500);
window.addEventListener('scroll', trackScrollDebounced);
Event Queue for Reliability:
const eventQueue = [];
let isProcessing = false;
async function queueEvent(eventName, eventData) {
eventQueue.push({ eventName, eventData });
processQueue();
}
async function processQueue() {
if (isProcessing || eventQueue.length === 0) return;
isProcessing = true;
while (eventQueue.length > 0) {
const { eventName, eventData } = eventQueue.shift();
try {
sa_event(eventName, eventData);
await new Promise(resolve => setTimeout(resolve, 100)); // Rate limiting
} catch (error) {
console.error('Failed to track event:', error);
}
}
isProcessing = false;
}
Testing Event Tracking
Development Logging:
const isDev = process.env.NODE_ENV === 'development';
function trackEvent(eventName, eventData) {
if (isDev) {
console.group('Simple Analytics Event');
console.log('Event:', eventName);
console.log('Data:', eventData);
console.groupEnd();
}
if (window.sa_event) {
sa_event(eventName, eventData);
}
}
Browser Console Testing:
// Test in console
sa_event('test_event', {
test: true,
timestamp: new Date().toISOString()
});
// Check if Simple Analytics loaded
console.log(typeof window.sa_event); // Should be 'function'
Verify in Dashboard:
- Fire test event from your site
- Go to Simple Analytics dashboard
- Click "Events" in sidebar
- Look for your event name
- Click event to see metadata
Best Practices
- Use Descriptive Event Names:
newsletter_signupinstead ofsignup - Keep Names Consistent: Use
snake_caseorcamelCaseconsistently - Group Related Events:
checkout_started,checkout_completed,checkout_abandoned - Include Meaningful Metadata: Add context that helps understand the event
- Don't Over-Track: Focus on actionable events
- Avoid PII: Never include personal information in events
- Test Thoroughly: Verify events appear in dashboard
- Document Events: Maintain a list of tracked events
- Use Constants: Define event names as constants to avoid typos
- Handle Errors: Wrap tracking in try-catch
Privacy Considerations
What NOT to Track:
// BAD: Don't track PII
sa_event('signup', {
email: 'user@example.com', // Don't
name: 'John Doe', // Don't
address: '123 Main St' // Don't
});
// GOOD: Track without PII
sa_event('signup', {
source: 'landing_page',
plan: 'free'
});
Respecting User Privacy:
// Check for consent if needed
function trackWithConsent(eventName, eventData) {
const hasConsent = localStorage.getItem('analytics_consent') === 'true';
if (hasConsent && window.sa_event) {
sa_event(eventName, eventData);
}
}
Event Naming Constants
// Define constants to avoid typos
const EVENTS = {
SIGNUP: 'user_signup',
LOGIN: 'user_login',
PURCHASE: 'purchase_completed',
CART_ADD: 'add_to_cart',
DOWNLOAD: 'file_download',
SEARCH: 'search_performed',
VIDEO_PLAY: 'video_play',
FORM_SUBMIT: 'form_submit'
};
// Usage
sa_event(EVENTS.SIGNUP, {
source: 'homepage'
});
sa_event(EVENTS.PURCHASE, {
value: 99.99,
currency: 'USD'
});
Viewing Events in Dashboard
Events Page:
- Log into Simple Analytics dashboard
- Click "Events" in sidebar
- View event list with counts
- Click event name to see metadata breakdown
- Filter by date range
- Export events data
Event Metadata Analysis:
- Click specific event to see all metadata values
- See frequency of each metadata value
- Filter events by metadata
- Track trends over time
Common Pitfalls
- Tracking Before Script Loads: Always check
window.sa_eventexists - Typos in Event Names: Use constants or TypeScript for type safety
- Too Much Metadata: Keep metadata focused and relevant
- Forgetting to Test: Always verify events appear in dashboard
- Inconsistent Naming: Stick to one naming convention
Summary
Simple Analytics event tracking is:
- Simple: Single function call
- Privacy-First: No cookies or user identification
- Flexible: Track any custom action
- Lightweight: Minimal performance impact
- Actionable: Focus on meaningful business events
Track what matters, respect user privacy, and gain valuable insights into how users interact with your product - all without the complexity of traditional analytics platforms.