Overview
Event tracking in Fathom Analytics is built around goals - specific actions you want to measure and optimize. Unlike complex event taxonomies in other platforms, Fathom keeps it simple: create a goal, get an ID, track it with fathom.trackGoal().
This guide covers everything from creating your first goal to implementing advanced event tracking patterns across your website or application.
Goal-Based Event Model
How Fathom Goals Work
Traditional analytics:
// Google Analytics - complex event structure
gtag('event', 'signup', {
event_category: 'engagement',
event_label: 'newsletter',
value: 1
});
Fathom Analytics:
// Simple goal tracking
fathom.trackGoal('SIGNUP01', 0);
Goal Creation Process
1. Create Goal in Dashboard:
- Log into Fathom Analytics
- Go to Settings > Goals
- Click Create Goal
- Enter goal name (e.g., "Newsletter Signup")
- Copy the Goal ID (e.g.,
SIGNUP01)
2. Implement Tracking:
<button 0)">
Sign Up
</button>
3. Verify in Dashboard:
- Visit Goals section
- Trigger the event
- Confirm it appears in reports
Core Event Tracking Patterns
Button Clicks
Inline onclick:
<button 0)">
Click Me
</button>
Event listener (recommended):
document.getElementById('signup-btn').addEventListener('click', function() {
fathom.trackGoal('SIGNUP01', 0);
});
Multiple buttons:
document.querySelectorAll('.cta-button').forEach(function(button) {
button.addEventListener('click', function() {
fathom.trackGoal('CTACLICK', 0);
});
});
Form Submissions
Basic form tracking:
document.getElementById('contact-form').addEventListener('submit', function(e) {
fathom.trackGoal('FORMSUBM', 0);
// Form continues to submit normally
});
Prevent premature form submission:
document.getElementById('signup-form').addEventListener('submit', function(e) {
e.preventDefault();
// Track goal
fathom.trackGoal('SIGNUP01', 0);
// Submit after small delay
setTimeout(() => {
e.target.submit();
}, 100);
});
Async form handling:
document.getElementById('newsletter-form').addEventListener('submit', async function(e) {
e.preventDefault();
try {
// Submit form data
const formData = new FormData(e.target);
const response = await fetch('/api/subscribe', {
method: 'POST',
body: formData
});
if (response.ok) {
// Track successful subscription
fathom.trackGoal('EMAILSUB', 0);
// Show success message
alert('Thanks for subscribing!');
}
} catch (error) {
console.error('Subscription failed:', error);
}
});
Link Clicks
External links:
<a href="https://external-site.com" 0)">
Visit Partner Site
</a>
Download links:
<a href="/files/whitepaper.pdf" 0)">
Download Whitepaper
</a>
All download links automatically:
document.querySelectorAll('a[href$=".pdf"], a[href$=".zip"], a[href$=".doc"]')
.forEach(function(link) {
link.addEventListener('click', function() {
fathom.trackGoal('DOWNLOAD', 0);
});
});
Scroll Tracking
Track scroll depth:
let scrollTracked = {
'50': false,
'75': false,
'100': false
};
window.addEventListener('scroll', function() {
const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
if (scrollPercent >= 50 && !scrollTracked['50']) {
fathom.trackGoal('SCROLL50', 0);
scrollTracked['50'] = true;
}
if (scrollPercent >= 75 && !scrollTracked['75']) {
fathom.trackGoal('SCROLL75', 0);
scrollTracked['75'] = true;
}
if (scrollPercent >= 100 && !scrollTracked['100']) {
fathom.trackGoal('SCROLL100', 0);
scrollTracked['100'] = true;
}
});
Debounced scroll tracking:
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
const trackScroll = debounce(function() {
const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
if (scrollPercent >= 75 && !window.scroll75Tracked) {
fathom.trackGoal('SCROLL75', 0);
window.scroll75Tracked = true;
}
}, 500);
window.addEventListener('scroll', trackScroll);
Video Tracking
YouTube embeds:
// Using YouTube IFrame API
let player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('video-player', {
events: {
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerStateChange(event) {
if (event.data === YT.PlayerState.PLAYING) {
fathom.trackGoal('VIDEOPLY', 0);
}
if (event.data === YT.PlayerState.ENDED) {
fathom.trackGoal('VIDEOEND', 0);
}
}
HTML5 video:
const video = document.querySelector('video');
video.addEventListener('play', function() {
fathom.trackGoal('VIDEOPLY', 0);
});
video.addEventListener('ended', function() {
fathom.trackGoal('VIDEOEND', 0);
});
// Track 25%, 50%, 75% milestones
let milestones = { 25: false, 50: false, 75: false };
video.addEventListener('timeupdate', function() {
const percent = (video.currentTime / video.duration) * 100;
Object.keys(milestones).forEach(milestone => {
if (percent >= milestone && !milestones[milestone]) {
fathom.trackGoal(`VIDEO${milestone}`, 0);
milestones[milestone] = true;
}
});
});
Modal/Popup Tracking
Track modal opens:
function openModal() {
fathom.trackGoal('MODALOPN', 0);
// Show modal
document.getElementById('modal').classList.add('active');
}
function closeModal() {
fathom.trackGoal('MODALCLS', 0);
// Hide modal
document.getElementById('modal').classList.remove('active');
}
Exit-intent popup:
let exitIntentShown = false;
document.addEventListener('mouseleave', function(e) {
if (e.clientY < 0 && !exitIntentShown) {
// Show exit-intent popup
showExitPopup();
// Track that exit-intent was shown
fathom.trackGoal('EXITPOP', 0);
exitIntentShown = true;
}
});
Ecommerce Event Tracking
Product Interactions
Product page view:
// On product detail page
const productId = document.querySelector('[data-product-id]')?.dataset.productId;
if (productId) {
fathom.trackGoal('PRODVIEW', 0);
}
Add to cart:
function addToCart(productId, price) {
// Add to cart logic...
// Track event
fathom.trackGoal('ADDCART', 0);
// Optional: track high-value adds separately
if (price > 100) {
fathom.trackGoal('ADDCART_HIGH', 0);
}
}
Remove from cart:
function removeFromCart(productId) {
// Remove from cart logic...
// Track removal
fathom.trackGoal('REMCART', 0);
}
Checkout Flow
Checkout initiated:
// On checkout page load
fathom.trackGoal('CHECKOUT', 0);
Shipping method selected:
document.querySelectorAll('input[name="shipping"]').forEach(function(radio) {
radio.addEventListener('change', function() {
if (this.value === 'express') {
fathom.trackGoal('SHIP_EXPR', 0);
} else {
fathom.trackGoal('SHIP_STD', 0);
}
});
});
Payment method selected:
document.querySelectorAll('input[name="payment"]').forEach(function(radio) {
radio.addEventListener('change', function() {
const goalIds = {
'credit_card': 'PAY_CARD',
'paypal': 'PAY_PAYPAL',
'crypto': 'PAY_CRYPTO'
};
const goalId = goalIds[this.value];
if (goalId) {
fathom.trackGoal(goalId, 0);
}
});
});
Purchase Completion
With revenue tracking:
// On order confirmation page
function trackPurchase(orderTotal, orderId) {
// Convert to cents
const cents = Math.round(orderTotal * 100);
// Track purchase with revenue
fathom.trackGoal('PURCHASE', cents);
console.log(`Tracked purchase: Order ${orderId}, Revenue: $${orderTotal}`);
}
// Usage
const orderTotal = 149.99;
const orderId = 'ORD-12345';
trackPurchase(orderTotal, orderId);
By product category:
function trackPurchaseByCategory(category, orderTotal) {
const cents = Math.round(orderTotal * 100);
const goalIds = {
'electronics': 'PURCH_ELEC',
'clothing': 'PURCH_CLTH',
'home': 'PURCH_HOME'
};
const goalId = goalIds[category] || 'PURCHASE';
fathom.trackGoal(goalId, cents);
}
Subscription Events
Trial started:
fathom.trackGoal('TRIAL', 0);
Subscription purchased:
function trackSubscription(plan, monthlyPrice) {
const cents = Math.round(monthlyPrice * 100);
const goalIds = {
'basic': 'SUB_BASIC',
'pro': 'SUB_PRO',
'enterprise': 'SUB_ENT'
};
const goalId = goalIds[plan] || 'SUBSCRIBE';
fathom.trackGoal(goalId, cents);
}
// Usage
trackSubscription('pro', 49.99);
Cancellation:
fathom.trackGoal('CANCEL', 0);
Advanced Tracking Patterns
Conditional Tracking
Track based on user state:
function trackSignup(userType) {
// Different goals for different user types
const goalIds = {
'individual': 'SIGNUP_IND',
'business': 'SIGNUP_BIZ',
'enterprise': 'SIGNUP_ENT'
};
const goalId = goalIds[userType];
if (goalId) {
fathom.trackGoal(goalId, 0);
}
}
Track based on page context:
// Track CTA clicks differently by page
function trackCTA() {
const page = window.location.pathname;
const goalIds = {
'/': 'CTA_HOME',
'/pricing': 'CTA_PRICING',
'/features': 'CTA_FEATURES'
};
const goalId = goalIds[page] || 'CTA_OTHER';
fathom.trackGoal(goalId, 0);
}
Time-Based Tracking
Track time on page:
let timeOnPage = 0;
let timerInterval;
// Start timer when page loads
window.addEventListener('load', function() {
timerInterval = setInterval(function() {
timeOnPage += 10;
// Track at 30 seconds
if (timeOnPage === 30 && !window.time30tracked) {
fathom.trackGoal('TIME30S', 0);
window.time30tracked = true;
}
// Track at 60 seconds
if (timeOnPage === 60 && !window.time60tracked) {
fathom.trackGoal('TIME60S', 0);
window.time60tracked = true;
}
}, 10000); // Check every 10 seconds
});
// Stop timer when user leaves
window.addEventListener('beforeunload', function() {
clearInterval(timerInterval);
});
Funnel Tracking
Multi-step funnel:
// Step 1: Landing
fathom.trackGoal('FUNNEL_1_LAND', 0);
// Step 2: Pricing viewed
fathom.trackGoal('FUNNEL_2_PRICE', 0);
// Step 3: Signup started
fathom.trackGoal('FUNNEL_3_SIGNUP', 0);
// Step 4: Account created
fathom.trackGoal('FUNNEL_4_ACCOUNT', 0);
// Step 5: Conversion
fathom.trackGoal('FUNNEL_5_CONVERT', 4900);
Analyze in dashboard:
- Compare counts: 1000 → 400 → 200 → 150 → 100
- Calculate conversion rates at each step
- Identify biggest drop-offs
A/B Test Tracking
Track variant views:
// Assign variant (would typically be from A/B testing tool)
const variant = Math.random() < 0.5 ? 'A' : 'B';
// Track variant view
fathom.trackGoal(`VARIANT_${variant}`, 0);
// Track conversion by variant
function trackConversion() {
const variant = sessionStorage.getItem('variant') || 'control';
fathom.trackGoal(`CONVERT_${variant}`, 0);
}
Framework-Specific Implementations
React
Track on component mount:
import { useEffect } from 'react';
function ProductPage({ productId }) {
useEffect(() => {
if (window.fathom) {
window.fathom.trackGoal('PRODVIEW', 0);
}
}, [productId]);
return <div>Product details...</div>;
}
Track button click:
function SignupButton() {
const handleClick = () => {
if (window.fathom) {
window.fathom.trackGoal('SIGNUP01', 0);
}
// Continue with signup logic...
};
return <button Up</button>;
}
Vue.js
Track in methods:
<template>
<button @click="trackAndSubmit">Submit</button>
</template>
<script>
export default {
methods: {
trackAndSubmit() {
if (window.fathom) {
window.fathom.trackGoal('FORMSUBM', 0);
}
// Continue with form submission...
}
}
}
</script>
Angular
Track in component:
import { Component } from '@angular/core';
@Component({
selector: 'app-signup',
template: '<button (click)="handleSignup()">Sign Up</button>'
})
export class SignupComponent {
handleSignup() {
if (typeof window !== 'undefined' && (window as any).fathom) {
(window as any).fathom.trackGoal('SIGNUP01', 0);
}
// Continue with signup logic...
}
}
Best Practices
Error Handling
function trackGoalSafely(goalId, value = 0) {
try {
if (typeof window !== 'undefined' && window.fathom) {
window.fathom.trackGoal(goalId, value);
} else {
console.warn('Fathom not loaded');
}
} catch (error) {
console.error('Goal tracking failed:', error);
}
}
Centralized Tracking
// analytics.js
export const GOALS = {
SIGNUP: 'SIGNUP01',
PURCHASE: 'PURCHASE',
DOWNLOAD: 'DOWNLOAD',
TRIAL: 'TRIAL123'
};
export function trackGoal(goalId, value = 0) {
if (window.fathom) {
window.fathom.trackGoal(goalId, value);
}
}
// Usage
import { GOALS, trackGoal } from './analytics';
trackGoal(GOALS.SIGNUP, 0);
Testing & Validation
Development logging:
const isDev = process.env.NODE_ENV === 'development';
function trackGoal(goalId, value = 0) {
if (isDev) {
console.log(`[Fathom] Goal: ${goalId}, Value: ${value}`);
}
if (window.fathom) {
window.fathom.trackGoal(goalId, value);
}
}
Verify in dashboard:
- Open Fathom dashboard
- Click "Current visitors"
- Trigger event on your site
- Confirm goal appears in real-time
Conclusion
Fathom's goal-based event tracking is simple yet powerful. By creating descriptive goals and implementing clean tracking code, you can measure everything that matters to your business while maintaining privacy and performance.
Additional Resources: