Deployment Strategy
- Embed the Fathom script in the
<head>withdata-siteattribute 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-siteset 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: Alwayshttps://cdn.usefathom.com/script.jsdata-site: Your Site ID from Fathom dashboarddefer: Ensures non-blocking load
Custom Domain Installation (Recommended)
Setup DNS:
- Create CNAME record:
stats.yourdomain.com→cdn.usefathom.com - Add custom domain in Fathom dashboard: Settings > Custom Domain
- 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
- Install "Fathom Analytics" plugin from WordPress.org
- Go to Settings > Fathom Analytics
- Enter Site ID:
ABCDEFGH - 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:
- Go to Settings > Code Injection
- Add to Site Header:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
Webflow
Custom Code:
- Go to Project Settings > Custom Code
- Add to Head Code:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
Squarespace
Code Injection:
- Go to Settings > Advanced > Code Injection
- Add to Header:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
Shopify
Edit Theme:
- Go to Online Store > Themes
- Click Actions > Edit Code
- Open Layout > theme.liquid
- Add before
</head>:
<script src="https://cdn.usefathom.com/script.js" data-site="ABCDEFGH" defer></script>
Wix
Tracking Code:
- Go to Settings > Custom Code
- Click Add Custom Code
- Paste Fathom script
- 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
- Open Fathom dashboard
- Click "Current visitors" (top right)
- Visit your website in another tab/window
- Confirm pageview appears within 30 seconds
- Trigger a goal and verify it appears
Network Debugging
- Open browser DevTools (F12)
- Go to Network tab
- Filter for "fathom" or "usefathom"
- Look for:
- Script load:
script.js(200 OK) - Pageviews: POST to
/api/event(200 OK) - Goals: POST to
/api/eventwith goal data (200 OK)
- Script load:
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.