Fathom Analytics Install / Embed Tag or SDK | OpsBlu Docs

Fathom Analytics Install / Embed Tag or SDK

Deployment approach for installing Fathom's tag or SDK across web, mobile, and server environments.

Deployment Strategy

  • Embed the Fathom script in the <head> with data-site attribute set to your Site ID.
  • For custom domain tracking (bypassing ad blockers), configure DNS and use your own subdomain (e.g., stats.yourdomain.com).
  • For SPAs, either enable automatic hash tracking or manually call fathom.trackPageview() on route changes.
  • If consent is required by policy, wrap the script load behind a consent gate (though Fathom is GDPR-compliant without consent).

Tag Manager Deployment

  • Load the Fathom script via Tag Management System with data-site set from environment variables or constants.
  • Create custom HTML tags for manual pageview calls on SPA route changes if automatic tracking is insufficient.
  • Insert consent checks before firing Fathom tags when required by organizational policy.
  • Use version control and testing capabilities of tag managers for safer deployments.

Direct Embed or Hard-Coded Scripts

Basic Website Installation

Standard Implementation:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Your Website</title>

  <!-- Fathom Analytics - Place before closing </head> tag -->
  <script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

  <!-- Rest of head content -->
</head>
<body>
  <!-- Your content -->
</body>
</html>

Key Attributes:

  • src: Always https://cdn.usefathom.com/script.js
  • data-site: Your Site ID from Fathom dashboard
  • defer: Ensures non-blocking load

Setup DNS:

  1. Create CNAME record: stats.yourdomain.comcdn.usefathom.com
  2. Add custom domain in Fathom dashboard: Settings > Custom Domain
  3. Wait for DNS propagation (up to 48 hours)

Implementation:

<!-- Use your custom domain instead of cdn.usefathom.com -->
<script src="https://stats.yourdomain.com/script.js" data-site="ABCDEFGH" defer></script>

Benefits:

  • Bypasses ad blockers
  • First-party tracking (higher accuracy)
  • Professional appearance
  • Better data quality

Excluded Domains

<!-- Don't track on localhost or staging -->
<script src="https://cdn.usefathom.com/script.js"
        data-site="ABCDEFGH"
        data-excluded-domains="localhost,staging.yourdomain.com,127.0.0.1"
        defer></script>

Honor Do Not Track (Optional)

<!-- Respect browser DNT setting -->
<script src="https://cdn.usefathom.com/script.js"
        data-site="ABCDEFGH"
        data-honor-dnt="true"
        defer></script>

SPA Hash-Based Routing

<!-- Automatically track hash changes as pageviews -->
<script src="https://cdn.usefathom.com/script.js"
        data-site="ABCDEFGH"
        data-auto="false"
        data-spa="hash"
        defer></script>

CMS Platform Installations

WordPress

Option 1: Official Plugin

  1. Install "Fathom Analytics" plugin from WordPress.org
  2. Go to Settings > Fathom Analytics
  3. Enter Site ID: ABCDEFGH
  4. Save changes

Option 2: Manual Theme Integration

<!-- Add to header.php before </head> -->
<?php if (!is_user_logged_in() && !is_admin()) : ?>
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
<?php endif; ?>

functions.php Method:

// Add to functions.php
function add_fathom_analytics() {
    if (!is_user_logged_in() && !is_admin()) {
        ?>
        <script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
        <?php
    }
}
add_action('wp_head', 'add_fathom_analytics');

Ghost

Code Injection:

  1. Go to Settings > Code Injection
  2. Add to Site Header:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Webflow

Custom Code:

  1. Go to Project Settings > Custom Code
  2. Add to Head Code:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Squarespace

Code Injection:

  1. Go to Settings > Advanced > Code Injection
  2. Add to Header:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Shopify

Edit Theme:

  1. Go to Online Store > Themes
  2. Click Actions > Edit Code
  3. Open Layout > theme.liquid
  4. Add before </head>:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Wix

Tracking Code:

  1. Go to Settings > Custom Code
  2. Click Add Custom Code
  3. Paste Fathom script
  4. Set to load on All Pages in Head

JavaScript Framework SDKs

React / Next.js

Install Package:

npm install fathom-client

React Router Setup:

// App.js
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import * as Fathom from 'fathom-client';

function App() {
  const location = useLocation();

  // Initialize on mount
  useEffect(() => {
    Fathom.load('ABCDEFGH', {
      includedDomains: ['yourdomain.com'],
      url: 'https://cdn.usefathom.com/script.js', // or custom domain
    });
  }, []);

  // Track pageviews on route change
  useEffect(() => {
    Fathom.trackPageview();
  }, [location]);

  return (
    <Router>
      {/* Your routes */}
    </Router>
  );
}

export default App;

Next.js Pages Router (_app.js):

import { useEffect } from 'react';
import { useRouter } from 'next/router';
import * as Fathom from 'fathom-client';

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  useEffect(() => {
    // Initialize Fathom
    Fathom.load('ABCDEFGH', {
      includedDomains: ['yourdomain.com'],
    });

    function onRouteChangeComplete() {
      Fathom.trackPageview();
    }

    // Track pageviews
    router.events.on('routeChangeComplete', onRouteChangeComplete);

    return () => {
      router.events.off('routeChangeComplete', onRouteChangeComplete);
    };
  }, [router.events]);

  return <Component {...pageProps} />;
}

export default MyApp;

Next.js App Router (layout.js):

'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import * as Fathom from 'fathom-client';

export default function RootLayout({ children }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    Fathom.load('ABCDEFGH', {
      includedDomains: ['yourdomain.com'],
    });
  }, []);

  useEffect(() => {
    Fathom.trackPageview();
  }, [pathname, searchParams]);

  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Vue.js

Install Package:

npm install fathom-client

Setup (main.js):

import { createApp } from 'vue';
import { createRouter } from 'vue-router';
import * as Fathom from 'fathom-client';
import App from './App.vue';
import router from './router';

// Initialize Fathom
Fathom.load('ABCDEFGH', {
  includedDomains: ['yourdomain.com'],
});

// Track pageviews on route change
router.afterEach(() => {
  Fathom.trackPageview();
});

createApp(App).use(router).mount('#app');

Composition API:

<script setup>
import { onMounted } from 'vue';
import * as Fathom from 'fathom-client';

onMounted(() => {
  // Track goal on component mount
  Fathom.trackGoal('PAGEVIEW', 0);
});
</script>

Angular

Install Package:

npm install fathom-client

Setup (app.component.ts):

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
import * as Fathom from 'fathom-client';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  constructor(private router: Router) {}

  ngOnInit() {
    // Initialize Fathom
    Fathom.load('ABCDEFGH', {
      includedDomains: ['yourdomain.com']
    });

    // Track pageviews on navigation
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe(() => {
      Fathom.trackPageview();
    });
  }
}

Gatsby

Install Plugin:

npm install gatsby-plugin-fathom

gatsby-config.js:

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-fathom',
      options: {
        siteId: 'ABCDEFGH',
        // Optional settings
        includedDomains: ['yourdomain.com'],
        honorDNT: false,
      },
    },
  ],
};

Mobile & OTT SDKs

Mobile Web

Use the standard JavaScript embed for mobile web:

<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>

Fathom automatically detects mobile devices and tracks appropriately.

Native Mobile Apps (iOS/Android)

Use Events API:

Fathom doesn't provide native mobile SDKs. For native apps, use the Events API:

iOS (Swift):

func trackFathomPageview(url: String) {
    let json: [String: Any] = [
        "site": "ABCDEFGH",
        "name": "pageview",
        "url": url
    ]

    let jsonData = try? JSONSerialization.data(withJSONObject: json)

    var request = URLRequest(url: URL(string: "https://cdn.usefathom.com/api/event")!)
    request.httpMethod = "POST"
    request.httpBody = jsonData
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    URLSession.shared.dataTask(with: request).resume()
}

Android (Kotlin):

fun trackFathomPageview(url: String) {
    val json = JSONObject()
    json.put("site", "ABCDEFGH")
    json.put("name", "pageview")
    json.put("url", url)

    val client = OkHttpClient()
    val body = RequestBody.create(
        MediaType.parse("application/json"),
        json.toString()
    )

    val request = Request.Builder()
        .url("https://cdn.usefathom.com/api/event")
        .post(body)
        .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {}
        override fun onFailure(call: Call, e: IOException) {}
    })
}

React Native

// analytics.js
export async function trackFathomPageview(url) {
  try {
    await fetch('https://cdn.usefathom.com/api/event', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        site: 'ABCDEFGH',
        name: 'pageview',
        url: url,
      }),
    });
  } catch (error) {
    console.error('Fathom tracking failed:', error);
  }
}

// Usage in component
import { trackFathomPageview } from './analytics';

useEffect(() => {
  trackFathomPageview('screen://HomeScreen');
}, []);

Server-Side Collection

Node.js

const fetch = require('node-fetch');

async function trackFathomEvent(eventType, url, goalId = null, value = 0) {
  const payload = {
    site: 'ABCDEFGH',
    name: eventType, // 'pageview' or 'goal'
    url: url,
  };

  if (goalId) {
    payload.goal = goalId;
    payload.value = value;
  }

  try {
    const response = await fetch('https://cdn.usefathom.com/api/event', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': 'YourApp/1.0',
      },
      body: JSON.stringify(payload),
    });

    return response.ok;
  } catch (error) {
    console.error('Fathom tracking error:', error);
    return false;
  }
}

// Usage
await trackFathomEvent('pageview', 'https://yourdomain.com/page');
await trackFathomEvent('goal', 'https://yourdomain.com/checkout', 'PURCHASE', 9999);

Python

import requests

def track_fathom_event(event_type, url, goal_id=None, value=0):
    payload = {
        'site': 'ABCDEFGH',
        'name': event_type,
        'url': url
    }

    if goal_id:
        payload['goal'] = goal_id
        payload['value'] = value

    try:
        response = requests.post(
            'https://cdn.usefathom.com/api/event',
            json=payload,
            headers={'User-Agent': 'YourApp/1.0'}
        )
        return response.status_code == 200
    except Exception as e:
        print(f'Fathom tracking error: {e}')
        return False

# Usage
track_fathom_event('pageview', 'https://yourdomain.com/page')
track_fathom_event('goal', 'https://yourdomain.com/checkout', 'PURCHASE', 9999)

PHP

<?php
function trackFathomEvent($eventType, $url, $goalId = null, $value = 0) {
    $payload = [
        'site' => 'ABCDEFGH',
        'name' => $eventType,
        'url' => $url
    ];

    if ($goalId) {
        $payload['goal'] = $goalId;
        $payload['value'] = $value;
    }

    $ch = curl_init('https://cdn.usefathom.com/api/event');
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'User-Agent: YourApp/1.0'
    ]);

    $result = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return $httpCode === 200;
}

// Usage
trackFathomEvent('pageview', 'https://yourdomain.com/page');
trackFathomEvent('goal', 'https://yourdomain.com/checkout', 'PURCHASE', 9999);
?>

Validation Checklist

After installation, verify proper implementation:

  • Script loads successfully: Check browser DevTools Network tab
  • Pageview tracked: Check Fathom dashboard real-time view
  • Site ID correct: Matches dashboard configuration
  • No console errors: Check browser console for JavaScript errors
  • defer attribute present: Ensures non-blocking load
  • Goals appear in dashboard: Test goal tracking and verify in Goals section
  • Cross-browser tested: Test in Chrome, Safari, Firefox, Edge
  • Mobile tested: Test on iOS and Android devices
  • Network requests succeed: 200 OK responses to /api/event

Real-Time Testing

  1. Open Fathom dashboard
  2. Click "Current visitors" (top right)
  3. Visit your website in another tab/window
  4. Confirm pageview appears within 30 seconds
  5. Trigger a goal and verify it appears

Network Debugging

  1. Open browser DevTools (F12)
  2. Go to Network tab
  3. Filter for "fathom" or "usefathom"
  4. Look for:
    • Script load: script.js (200 OK)
    • Pageviews: POST to /api/event (200 OK)
    • Goals: POST to /api/event with goal data (200 OK)

Configuration Recommendations

Custom Domain: Set up a custom domain (e.g., analytics.yourdomain.com) to bypass ad blockers. In Fathom dashboard, go to Settings → Custom Domains → Add Domain. Point a CNAME record to cdn.usefathom.com. Update your script src to use the custom domain. This typically recovers 15-25% of blocked pageviews.

EU Isolation: Enable EU isolation if you serve EU users and want data processed entirely within EU infrastructure. Toggle in Site Settings → Privacy → EU Isolation. When enabled, all data processing occurs in Frankfurt (AWS eu-central-1) with no data transfer to US servers. This satisfies most GDPR data transfer requirements without additional legal mechanisms.

Excluded Domains: Add localhost, staging domains, and internal tools to Site Settings → Excluded Domains. This prevents development traffic from polluting production analytics. Fathom also automatically excludes traffic from the Fathom dashboard itself.

Goals: Create all goals in the Fathom dashboard (Site Settings → Goals) before deploying tracking code. Goals can be pageview-based (triggers on URL match) or custom event-based (triggers on plausible('EventName') calls — note: Fathom uses the same API). Revenue tracking requires passing _revenue in cents: fathom.trackEvent('Purchase', { _revenue: 2999 }).

SPA Tracking: For React, Vue, or Next.js apps, use the official fathom-client npm package which handles virtual pageviews automatically on route changes. For custom SPAs, call fathom.trackPageview() after each client-side navigation.