Fathom Analytics Data Layer Setup | OpsBlu Docs

Fathom Analytics Data Layer Setup

How to configure data layer variables and user properties for Fathom Analytics. Covers data preparation, custom properties, event enrichment, and.

Overview

Unlike traditional analytics platforms that use complex data layers (like Google Tag Manager's dataLayer), Fathom Analytics follows a simpler, privacy-first approach. Fathom doesn't support a persistent data layer or user properties, maintaining its commitment to privacy and simplicity.

This guide explains how to structure your data collection within Fathom's constraints and implement tracking patterns that provide valuable insights while respecting user privacy.

Understanding Fathom's Data Model

What Fathom Collects Automatically

Pageview Data (Collected by Default):

  • Page URL
  • Page title
  • Referrer source
  • Device type (desktop, mobile, tablet)
  • Browser type
  • Operating system
  • Country (from IP, then discarded)
  • Timestamp

What Fathom Does NOT Collect:

  • Cookies
  • User IDs or identifiers
  • IP addresses (used for geolocation, then discarded)
  • Personal information
  • Cross-site tracking data
  • Browsing history
  • Device fingerprints

Privacy-First Constraints

Fathom intentionally limits data collection to protect privacy:

No Persistent User Properties:

  • Cannot store user attributes across sessions
  • Cannot identify returning users individually
  • Cannot build user profiles
  • Cannot track user journeys at individual level

No Traditional Data Layer:

  • No dataLayer.push() equivalent
  • No global object for storing context
  • No pre-population of event data

Aggregate Data Only:

  • All data is anonymized and aggregated
  • Individual user behavior not tracked
  • Session-level continuity not maintained across visits

Implementing Context with Goals

Since Fathom doesn't support a traditional data layer, use goal-based tracking with naming conventions to add context.

Pattern 1: Descriptive Goal Names

Create goals with contextual information in the name:

Examples:

// Product category tracking
fathom.trackGoal('PROD_ELECTRONICS', 0);
fathom.trackGoal('PROD_CLOTHING', 0);

// User type tracking
fathom.trackGoal('SIGNUP_FREE', 0);
fathom.trackGoal('SIGNUP_TRIAL', 0);
fathom.trackGoal('SIGNUP_PAID', 0);

// Feature usage
fathom.trackGoal('FEAT_EXPORT_CSV', 0);
fathom.trackGoal('FEAT_EXPORT_PDF', 0);

// Content type
fathom.trackGoal('CONTENT_ARTICLE', 0);
fathom.trackGoal('CONTENT_VIDEO', 0);
fathom.trackGoal('CONTENT_PODCAST', 0);

Dashboard organization: Goals appear as separate items, allowing you to see:

  • How many users exported CSV vs PDF
  • Free vs trial vs paid signups
  • Electronics vs clothing product views

Pattern 2: Programmatic Goal Selection

Use JavaScript to dynamically select goals based on page context:

// Product category from page metadata
const category = document.querySelector('[data-category]')?.dataset.category;
if (category) {
  const goalId = {
    'electronics': 'PROD_ELEC',
    'clothing': 'PROD_CLTH',
    'home': 'PROD_HOME'
  }[category];

  if (goalId) {
    fathom.trackGoal(goalId, 0);
  }
}
// User plan from logged-in state
function trackSignupByPlan(planType) {
  const goalIds = {
    'free': 'SIGNUP_FREE',
    'trial': 'SIGNUP_TRIAL',
    'pro': 'SIGNUP_PRO',
    'enterprise': 'SIGNUP_ENT'
  };

  const goalId = goalIds[planType];
  if (goalId) {
    fathom.trackGoal(goalId, 0);
  }
}

// Usage
trackSignupByPlan('trial');

Pattern 3: Combined Context Goals

Create compound goals for multi-dimensional tracking:

// Platform + Action
fathom.trackGoal('IOS_DOWNLOAD', 0);
fathom.trackGoal('ANDROID_DOWNLOAD', 0);
fathom.trackGoal('WEB_SIGNUP', 0);

// Source + Conversion
fathom.trackGoal('GOOGLE_CONVERT', 0);
fathom.trackGoal('TWITTER_CONVERT', 0);
fathom.trackGoal('EMAIL_CONVERT', 0);

// Price tier + Purchase
function trackPurchase(tier, amount) {
  const goalIds = {
    'basic': 'PURCHASE_BASIC',
    'pro': 'PURCHASE_PRO',
    'enterprise': 'PURCHASE_ENT'
  };

  const goalId = goalIds[tier];
  if (goalId) {
    fathom.trackGoal(goalId, amount);
  }
}

Revenue Tracking as Data Context

Revenue values provide an additional dimension of data:

Basic Revenue Tracking

// Track purchase with revenue
const orderTotal = 149.99;
const cents = Math.round(orderTotal * 100);
fathom.trackGoal('PURCHASE', cents);

Revenue by Product Category

// Create separate purchase goals per category
function trackPurchaseByCategory(category, amount) {
  const goalIds = {
    'electronics': 'PURCH_ELEC',
    'clothing': 'PURCH_CLTH',
    'home': 'PURCH_HOME'
  };

  const cents = Math.round(amount * 100);
  const goalId = goalIds[category];

  if (goalId) {
    fathom.trackGoal(goalId, cents);
  }
}

// Usage
trackPurchaseByCategory('electronics', 299.99);

Revenue by Plan Tier

// Track subscription by plan
function trackSubscription(plan, monthlyPrice) {
  const goalIds = {
    'basic': 'SUB_BASIC',
    'pro': 'SUB_PRO',
    'enterprise': 'SUB_ENT'
  };

  const cents = Math.round(monthlyPrice * 100);
  const goalId = goalIds[plan];

  if (goalId) {
    fathom.trackGoal(goalId, cents);
  }
}

// Usage
trackSubscription('pro', 49.99);

Page Metadata for Context

Use page metadata to inform goal tracking:

HTML Data Attributes

<!-- Product page -->
<body data-page-type="product" data-category="electronics" data-price-tier="premium">

<script>
// Extract metadata
const pageType = document.body.dataset.pageType;
const category = document.body.dataset.category;
const priceTier = document.body.dataset.priceTier;

// Track contextual goal
if (pageType === 'product' && category) {
  const goalId = `PRODUCT_${category.toUpperCase()}`;
  fathom.trackGoal(goalId, 0);
}
</script>

Meta Tags

<meta name="analytics:category" content="electronics">
<meta name="analytics:type" content="product">

<script>
const category = document.querySelector('meta[name="analytics:category"]')?.content;
const type = document.querySelector('meta[name="analytics:type"]')?.content;

if (category && type === 'product') {
  fathom.trackGoal(`PROD_${category.toUpperCase()}`, 0);
}
</script>

Session Context (Client-Side Only)

While Fathom doesn't persist user data, you can use client-side storage for session-level context:

sessionStorage for Temporary Context

// Track source on landing
const urlParams = new URLSearchParams(window.location.search);
const source = urlParams.get('utm_source');

if (source) {
  sessionStorage.setItem('landing_source', source);
}

// Use source context on conversion
function trackConversion() {
  const source = sessionStorage.getItem('landing_source') || 'direct';

  const goalIds = {
    'google': 'CONV_GOOGLE',
    'twitter': 'CONV_TWITTER',
    'email': 'CONV_EMAIL',
    'direct': 'CONV_DIRECT'
  };

  const goalId = goalIds[source];
  if (goalId) {
    fathom.trackGoal(goalId, 0);
  }
}

Important: This data is client-side only and not sent to Fathom. It's used to determine which goal to fire.

localStorage for Persistent Client Context

// Store plan selection
function selectPlan(planType) {
  localStorage.setItem('selected_plan', planType);
  fathom.trackGoal(`PLAN_SELECTED_${planType.toUpperCase()}`, 0);
}

// Use on signup
function trackSignup() {
  const plan = localStorage.getItem('selected_plan') || 'free';
  const goalId = `SIGNUP_${plan.toUpperCase()}`;

  fathom.trackGoal(goalId, 0);

  // Clean up
  localStorage.removeItem('selected_plan');
}

URL Parameters as Context

Use URL structure to add context without storing user data:

UTM Parameters

// Track campaign conversions
const urlParams = new URLSearchParams(window.location.search);
const campaign = urlParams.get('utm_campaign');

if (campaign) {
  // Track that this conversion came from specific campaign
  const goalId = `CONV_${campaign.toUpperCase().replace(/[^A-Z0-9]/g, '')}`;
  fathom.trackGoal(goalId, 0);
}

Query String Context

<!-- Link with context -->
<a href="/signup?plan=pro">Sign Up for Pro</a>

<script>
// On signup page
const urlParams = new URLSearchParams(window.location.search);
const plan = urlParams.get('plan') || 'free';

// Track signup with plan context
const goalId = `SIGNUP_${plan.toUpperCase()}`;
fathom.trackGoal(goalId, 0);
</script>

Ecommerce Data Patterns

Product Data

// On product page
const productData = {
  id: 'SKU12345',
  category: 'electronics',
  price: 299.99
};

// Track product view by category
fathom.trackGoal(`PRODVIEW_${productData.category.toUpperCase()}`, 0);

// Track add to cart with category context
function addToCart(product) {
  fathom.trackGoal(`ADDCART_${product.category.toUpperCase()}`, 0);
}

Order Data

// After successful purchase
const orderData = {
  total: 449.97,
  items: [
    { category: 'electronics', price: 299.99 },
    { category: 'accessories', price: 149.98 }
  ],
  shippingMethod: 'express'
};

// Track overall purchase
const cents = Math.round(orderData.total * 100);
fathom.trackGoal('PURCHASE', cents);

// Track by shipping method
const shippingGoal = `SHIP_${orderData.shippingMethod.toUpperCase()}`;
fathom.trackGoal(shippingGoal, 0);

// Track by dominant category
const dominantCategory = orderData.items.sort((a, b) => b.price - a.price)[0].category;
const categoryGoal = `PURCH_${dominantCategory.toUpperCase()}`;
fathom.trackGoal(categoryGoal, cents);

Event Sequencing Without User Tracking

Track funnel progression without identifying individuals:

Funnel Step Goals

// Landing page
fathom.trackGoal('FUNNEL_LANDING', 0);

// Pricing page
fathom.trackGoal('FUNNEL_PRICING', 0);

// Signup page
fathom.trackGoal('FUNNEL_SIGNUP', 0);

// Onboarding
fathom.trackGoal('FUNNEL_ONBOARD', 0);

// Conversion
fathom.trackGoal('FUNNEL_CONVERT', 4900);

Analysis: Compare goal counts to identify drop-off points:

  • 1000 FUNNEL_LANDING
  • 300 FUNNEL_PRICING (70% drop-off)
  • 150 FUNNEL_SIGNUP (50% drop-off)
  • 100 FUNNEL_ONBOARD (33% drop-off)
  • 75 FUNNEL_CONVERT (25% drop-off)

Micro-Conversions

// Track engagement levels
fathom.trackGoal('ENGAGE_SCROLL_50', 0);  // Scrolled 50%
fathom.trackGoal('ENGAGE_SCROLL_75', 0);  // Scrolled 75%
fathom.trackGoal('ENGAGE_VIDEO_PLAY', 0); // Played video
fathom.trackGoal('ENGAGE_VIDEO_COMPLETE', 0); // Completed video
fathom.trackGoal('ENGAGE_CTA_CLICK', 0);  // Clicked CTA

Centralized Tracking Configuration

Create a configuration object for organized tracking:

Tracking Configuration File

// analytics-config.js
export const FATHOM_GOALS = {
  // Signups
  SIGNUP_FREE: 'SIGNUP_FREE',
  SIGNUP_TRIAL: 'SIGNUP_TRIAL',
  SIGNUP_PRO: 'SIGNUP_PRO',

  // Purchases
  PURCHASE: 'PURCHASE',
  PURCHASE_ELECTRONICS: 'PURCH_ELEC',
  PURCHASE_CLOTHING: 'PURCH_CLTH',

  // Features
  FEATURE_EXPORT_CSV: 'FEAT_EXPORT_CSV',
  FEATURE_EXPORT_PDF: 'FEAT_EXPORT_PDF',

  // Funnel
  FUNNEL_LANDING: 'FUNNEL_LANDING',
  FUNNEL_PRICING: 'FUNNEL_PRICING',
  FUNNEL_SIGNUP: 'FUNNEL_SIGNUP',
};

export function trackGoal(goalId, value = 0) {
  if (window.fathom && typeof window.fathom.trackGoal === 'function') {
    window.fathom.trackGoal(goalId, value);
  }
}

export function trackPurchaseByCategory(category, amount) {
  const categoryGoals = {
    'electronics': FATHOM_GOALS.PURCHASE_ELECTRONICS,
    'clothing': FATHOM_GOALS.PURCHASE_CLOTHING,
  };

  const goalId = categoryGoals[category] || FATHOM_GOALS.PURCHASE;
  const cents = Math.round(amount * 100);

  trackGoal(goalId, cents);
}

Usage

import { FATHOM_GOALS, trackGoal, trackPurchaseByCategory } from './analytics-config';

// Simple goal tracking
trackGoal(FATHOM_GOALS.SIGNUP_FREE, 0);

// Purchase with category context
trackPurchaseByCategory('electronics', 299.99);

Best Practices

Goal Naming Conventions

Use consistent patterns:

// Category prefix
'SIGNUP_*'    // All signup-related goals
'PURCHASE_*'  // All purchase-related goals
'FEATURE_*'   // All feature usage goals
'FUNNEL_*'    // All funnel step goals

// Examples
'SIGNUP_FREE'
'SIGNUP_TRIAL'
'PURCHASE_ELECTRONICS'
'PURCHASE_CLOTHING'
'FEATURE_EXPORT'
'FUNNEL_LANDING'

Documentation

Maintain a tracking dictionary:

# Fathom Goal Dictionary

## Signups
- SIGNUP_FREE (ID: FREE001) - Free plan signup
- SIGNUP_TRIAL (ID: TRIAL01) - Trial plan signup
- SIGNUP_PRO (ID: PRO0001) - Pro plan signup

## Purchases
- PURCHASE (ID: PURCHASE) - Any purchase (with revenue)
- PURCH_ELEC (ID: PRCHELEC) - Electronics purchase (with revenue)
- PURCH_CLTH (ID: PRCHCLTH) - Clothing purchase (with revenue)

## Funnel
- FUNNEL_LANDING (ID: FUNLAND1) - Landing page view
- FUNNEL_PRICING (ID: FUNPRIC1) - Pricing page view
- FUNNEL_SIGNUP (ID: FUNSIGN1) - Signup page view

Testing

Validate goal firing:

// Development mode logging
const isDev = process.env.NODE_ENV === 'development';

function trackGoalWithLogging(goalId, value = 0) {
  if (isDev) {
    console.log(`[Fathom] Goal: ${goalId}, Value: ${value}`);
  }

  if (window.fathom) {
    window.fathom.trackGoal(goalId, value);
  }
}

Privacy Compliance

Already compliant:

  • No personal data collection needed
  • No cookie consent required
  • No user identification
  • All data anonymous and aggregated

Don't try to circumvent privacy:

// Don't attempt to track individual users
// Violates Fathom's privacy principles
sessionStorage.setItem('user_id', userId); // Don't do this

// Track aggregate behavior only
fathom.trackGoal('USER_SIGNUP', 0);

Limitations to Understand

Cannot Do

  • User-level journeys: Cannot track individual user paths
  • Persistent user properties: Cannot store attributes across sessions
  • Complex segmentation: Limited to goal-based tracking
  • Behavior cohorts: Cannot group users by behavior patterns
  • Multi-touch attribution: Cannot credit multiple touchpoints

Can Do

  • Aggregate funnel analysis: Compare goal counts for drop-off analysis
  • Category-based tracking: Use goals for different categories/types
  • Revenue by segment: Track revenue for different product/plan types
  • Campaign performance: Track conversions by source/campaign
  • Feature adoption: Track feature usage through goals

Conclusion

While Fathom doesn't support traditional data layers or user properties, its goal-based approach provides valuable aggregate insights while maintaining privacy. By using:

  • Descriptive goal naming for context
  • Programmatic goal selection based on page metadata
  • Revenue tracking for monetary context
  • Client-side session storage for funnel tracking (without sending to Fathom)
  • Centralized configuration for organized tracking

You can implement effective analytics that respect user privacy and provide actionable business insights.


Additional Resources: