/ astro-integrations / How to Use Fathom with Astro: Complete Guide
astro-integrations 10 min read

How to Use Fathom with Astro: Complete Guide

Step-by-step guide to integrating Fathom with your Astro website.

How to Use Fathom with Astro: Complete Guide

Fathom is a privacy-first web analytics platform that gives you the metrics you actually need without the complexity (or privacy concerns) of Google Analytics. It is GDPR, CCPA, and ePrivacy compliant out of the box, which means no cookie banners required. Fathom collects pageviews, referrers, device info, and goal conversions without tracking individual users or storing personal data.

For Astro sites, integrating Fathom takes about two minutes. You add a script tag and you are done. But there are a few patterns that make the integration work better, especially for sites with client-side navigation or islands architecture.

Versions verified for this guide (checked 2026-05-29): Astro 6.4.2 and the optional fathom-client helper at 3.7.2.

A note on Astro's rendering model, since it changed recently. Astro is on v6 now, and output: 'static' is the default, meaning your whole site is prerendered to static HTML unless you opt out. The old output: 'hybrid' value was removed in Astro 5, so do not reach for it. The valid output values today are 'static' and 'server'. Fathom is a client-side script that runs in the browser regardless of how a page is rendered, so for a normal blog or marketing site you do not need an adapter or on-demand rendering at all. You only add an adapter (and export const prerender = false on the specific pages, or output: 'server' project-wide) if those pages need server rendering for other reasons. The analytics script works the same either way.

Prerequisites

  • Node.js 22.12.0 or newer (Astro 6 dropped Node 18 and Node 20 entirely and now requires Node 22.12.0 or higher; odd-numbered releases are not supported)
  • An Astro project (npm create astro@latest)
  • A Fathom account (paid plans start at $15/mo for 100,000 pageviews; a 7-day free trial is available)
  • Your Fathom site ID (found in your Fathom dashboard under Settings, then Sites)

Installation

Fathom does not require any npm packages for basic tracking. It is a single script tag. However, if you want programmatic control for SPAs or custom events, install the framework-agnostic client helper:

npm install fathom-client

As of 2026-05-29 the current published version is fathom-client@3.7.2. The package name has not changed, so the install command above is current.

Configuration

The simplest approach is adding the Fathom script to your layout. Get your site ID from the Fathom dashboard.

Add it to .env for easy management:

PUBLIC_FATHOM_SITE_ID=ABCDEFGH

Create an analytics component:

---
// src/components/Analytics.astro
const siteId = import.meta.env.PUBLIC_FATHOM_SITE_ID;
---

{siteId && (
  <script
    src="https://cdn.usefathom.com/script.js"
    data-site={siteId}
    data-auto="true"
    data-spa="auto"
    defer
  ></script>
)}

Add this component to your base layout:

---
// src/layouts/BaseLayout.astro
import Analytics from "../components/Analytics.astro";
---

<html>
  <head>
    <Analytics />
    <!-- other head elements -->
  </head>
  <body>
    <slot />
  </body>
</html>

The data-spa="auto" attribute tells Fathom to automatically track page navigations in single-page app mode. According to the official Fathom embed docs, data-spa accepts three values: auto (use the HTML5 History API, falling back to hash-based change detection), history (History API only), and hash (hash-based routing only). The auto value is the right default and covers Astro's client-side navigation. There are a few other documented attributes worth knowing: data-auto="false" disables automatic pageview tracking (you then track manually, shown below), data-canonical="false" ignores any canonical tag and reports the actual URL, and data-honor-dnt="true" respects the visitor's Do Not Track header. Fathom is privacy-first regardless, so DNT honoring is optional rather than required.

Basic Usage

With the script tag in place, Fathom tracks pageviews automatically. No additional code needed for basic analytics.

For custom event tracking (goal conversions), use the fathom-client package:

---
// src/components/SignupButton.astro
---

<button id="signup-btn">Start Free Trial</button>

<script>
  import * as Fathom from "fathom-client";

  document.getElementById("signup-btn")?.addEventListener("click", () => {
    Fathom.trackEvent("signup_click");
  });
</script>

If you are using View Transitions (the client-side router introduced in Astro 3 and stable since), the data-spa="auto" script attribute already handles route changes for you in most cases. If you want full programmatic control instead, the official fathom-client README recommends a clear pattern: call Fathom.load(siteId, { auto: false }) once on initial load, then call Fathom.trackPageview() inside your route-change handler. The functions are safe to call even before the script finishes loading, so ordering is not a concern.

For Astro's client router, the navigation event to hook is astro:page-load (it fires on the first load and after every client-side navigation), which makes the manual wiring simple:

---
// src/components/Analytics.astro
const siteId = import.meta.env.PUBLIC_FATHOM_SITE_ID;
---

{siteId && (
  <script>
    import * as Fathom from "fathom-client";

    // Load once, with automatic pageview tracking off so we drive it ourselves.
    Fathom.load(import.meta.env.PUBLIC_FATHOM_SITE_ID, {
      auto: false,
    });

    // astro:page-load fires on the initial load AND after every
    // client-side navigation, so one handler covers both.
    document.addEventListener("astro:page-load", () => {
      Fathom.trackPageview();
    });
  </script>
)}

If you prefer to track only the post-swap moment of a transition rather than the full load lifecycle, astro:after-swap is also available; astro:page-load is the more common choice because it also covers the very first page load.

For tracking goal conversions with monetary values, trackEvent takes the event name plus an options object. The README documents _value (the event value in cents) and _site_id (override the site the event is attributed to):

// Track a purchase event worth $49.00
Fathom.trackEvent("purchase_complete", { _value: 4900 }); // _value is in cents

Note: the older trackGoal(code, cents) function is deprecated in fathom-client. Use trackEvent for all new code.

Set up goals in the Fathom dashboard to see conversion rates, revenue attribution, and funnel data alongside your pageview metrics.

Production Tips

  1. Custom domains for the tracking script (read the current status first). Fathom historically let you serve the script from a custom subdomain (for example stats.yourdomain.com) via a DNS CNAME, so it loads first-party and slips past blockers that target third-party analytics. As of this writing Fathom says major ad blockers now catch custom domains too, so the feature no longer reliably bypasses blocking; existing custom domains keep working but Fathom has stopped surfacing the setup in the script settings. Treat it as a legacy option rather than a recommended one, and check Fathom's current docs for the latest stance before relying on it.

  2. Track only in production. Wrap the analytics component in an environment check so you do not pollute your data with localhost pageviews during development.

{import.meta.env.PROD && siteId && (
  <script src="https://cdn.usefathom.com/script.js" data-site={siteId} defer></script>
)}
  1. Use the Fathom API for dashboards. Fathom has a REST API that lets you pull analytics data into custom dashboards or reporting tools. Use it to display visitor counts directly on your site or build internal dashboards.

  2. Set up email reports. Fathom sends weekly or monthly email summaries of your traffic. Enable these in your site settings so you stay aware of trends without logging into the dashboard every day.

  3. Exclude your own traffic. Fathom provides a "Block my visits" feature accessible from your site's settings. Use it on every device you regularly browse your site from to keep your analytics clean.

Common Errors and Fixes

No pageviews showing up at all. The most common cause is the PUBLIC_ prefix. Astro only exposes environment variables to client-side code when they are prefixed with PUBLIC_. If your variable is named FATHOM_SITE_ID instead of PUBLIC_FATHOM_SITE_ID, import.meta.env resolves to undefined in the browser and the script never renders. This is documented in Astro's environment variables guide.

The site ID is correct but events still do not record. Check whether you are excluding your own visits. Fathom's "Block my visits" feature (and the blockTrackingForMe() / enableTrackingForMe() functions in fathom-client) silently stops sending data from your device. If you turned it on while testing, your own pageviews will not appear. You can confirm the current state with isTrackingEnabled().

Localhost data polluting production. Wrap the script in import.meta.env.PROD so it only loads in production builds. During astro dev the variable is falsy, so no dev pageviews get sent. This is the recommended pattern and is shown in the Production Tips below.

Double-counting pageviews on client-side navigation. This happens when you leave automatic tracking on (data-auto defaults to true, or you call load() without auto: false) AND also call trackPageview() manually from a route-change handler. Pick one model. Either let data-spa="auto" handle everything automatically, or set auto: false and drive trackPageview() yourself. Do not do both.

Importing fathom-client in the frontmatter. fathom-client is browser-only code. It must live inside a <script> tag (which Astro bundles and ships to the client), not in the component frontmatter (the --- fences), which runs at build or request time on the server where window and document do not exist. Importing it in the frontmatter will throw at build time.

output: 'hybrid' in astro.config.mjs. If you copied an older config, this value was removed in Astro 5 and will error on Astro 6. Static is already the default, so for a Fathom-only setup you can simply delete the output line. Use 'static' or 'server' if you need to be explicit.

Official Docs and Examples

Alternatives to Consider

  • Plausible if you want a similar privacy-first analytics platform with a lower starting price ($9/mo) and a self-hosted option.
  • Umami if you want a free, open-source alternative you can self-host on your own server.
  • Google Analytics if you need advanced features like user flow visualization, e-commerce tracking, and integration with Google Ads (at the cost of privacy compliance).

Wrapping Up

Fathom is the analytics tool for developers who want useful data without the guilt of tracking users. The integration with Astro is trivial, the dashboard is clean, and you never have to show a cookie consent banner. For most Astro sites, blogs, documentation, marketing pages, Fathom provides all the insights you need (page views, referrers, top pages, devices) without the overwhelming complexity of Google Analytics. Paid plans start at $15/month for 100,000 pageviews with a 7-day free trial, a reasonable expense that saves you from dealing with GDPR compliance headaches.

Sources

All facts, versions, and APIs above were verified against these sources on 2026-05-29.