Fathom Analytics Event Tracking Setup | OpsBlu Docs

Fathom Analytics Event Tracking Setup

How to implement custom event tracking in Fathom Analytics. Covers event naming conventions, required and optional parameters, ecommerce events, debugging.

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:

  1. Log into Fathom Analytics
  2. Go to Settings > Goals
  3. Click Create Goal
  4. Enter goal name (e.g., "Newsletter Signup")
  5. 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);
  }
});

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:

  1. Open Fathom dashboard
  2. Click "Current visitors"
  3. Trigger event on your site
  4. 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: