Static site generators · 3 min setup

How to add analytics to a Gatsby site

To add VisitTrack to Gatsby, render the script tag with setHeadComponents in the onRenderBody API of gatsby-ssr.js (or .tsx). It's written into the head of every generated page, and navigations through Gatsby's <Link> are counted as pageviews automatically.

Updated

gatsby-ssr runs at build time for every page, which makes it the one place a site-wide tag belongs. The per-page Gatsby Head API is meant for page-specific tags; using it for analytics means remembering it on every template.

Gatsby at a glance

Where the tag goes
onRenderBody → setHeadComponents in gatsby-ssr.js
Navigation
<Link> / navigate() tracked automatically
Plugin needed
None
Local dev
gatsby develop on localhost is ignored
Custom events
window.visitrack() in components

How to install VisitTrack on Gatsby

  1. Step 1: Copy your site id

    Copy your snippet from VisitTrack's install step. Replace SITE_ID below.

  2. Step 2: Add onRenderBody to gatsby-ssr

    Create gatsby-ssr.js (or .tsx) at the project root if it doesn't exist. React needs a key on elements passed in an array.

    gatsby-ssr.js
    import * as React from "react";
    
    export const onRenderBody = ({ setHeadComponents }) => {
      setHeadComponents([
        <script
          key="visitrack"
          defer
          data-site="SITE_ID"
          src="https://visitrack.app/tracker.js"
        />,
      ]);
    };
  3. Step 3: Build and deploy

    Run gatsby build and deploy public/ (Netlify, Cloudflare Pages, Vercel or any static host). Restart gatsby develop after editing gatsby-ssr if you want to see the tag locally.

How to check VisitTrack is working on Gatsby

  1. Search public/index.html for tracker.js after the build.
  2. On the deployed site, move between pages with your nav; the VisitTrack live view and Pages tab update for each page.
  3. DevTools → Network shows one POST to visitrack.app/api/collect per navigation.

Gatsby's router changes the URL with history.pushState when visitors follow a <Link> or you call navigate(). The tracker wraps pushState and listens for popstate, so you don't need onRouteUpdate in gatsby-browser — adding a manual pageview there would double-count.

Prefetching (Gatsby loads page data when links enter the viewport) doesn't change the URL, so it never creates pageviews.

How to track custom events and goals in Gatsby

Call window.visitrack() in event handlers after the action succeeds; it only runs in the browser, so it's safe from Gatsby's build-time rendering as long as it isn't called during render.

src/components/Newsletter.jsx
export default function Newsletter() {
  async function onSubmit(e) {
    e.preventDefault();
    const res = await fetch("/api/subscribe", { method: "POST", body: new FormData(e.currentTarget) });
    if (res.ok) window.visitrack?.("newsletter_signup", { location: "footer" });
  }
  return (
    <form onSubmit={onSubmit}>
      <input name="email" type="email" required />
      <button data-vt-goal="newsletter_click">Subscribe</button>
    </form>
  );
}

data-vt-goal (clicks) and data-vt-scroll (sections scrolled into view) work as plain JSX attributes, and are re-scanned after client-side navigations.

Script options you might need on Gatsby

All optional — add them to the same tag. Full reference: script configuration.

AttributeWhat it does
data-cookielessStore nothing in the browser; the server derives a daily-rotating id. See cookieless mode.
data-exclude="/app/*"Never track these paths (comma-separated, * wildcard). Checked on every navigation.
data-allow-localTrack on localhost and *.local — for testing an install only.
data-debugLog every event the script sends, and why it skipped one, to the console.
data-auto="false"Turn off automatic events (downloads, contact clicks, form submits, rage clicks, JS errors, 404s).

Gatsby troubleshooting

Doubled pageviews

Remove any onRouteUpdate code that sends pageviews, and any second copy of the tag in a layout or the Head API.

window is not defined during build

A window.visitrack call ran during rendering. Move it into an event handler or useEffect.

The tag isn't in the HTML

gatsby-ssr changes need a fresh build or a dev-server restart. Check the file is at the project root and exports onRenderBody.

Old analytics plugin still sending pageviews

Remove gatsby-plugin-google-gtag or similar from gatsby-config if you're switching, so both don't run on every navigation.

Gatsby analytics FAQ

Do I need a Gatsby plugin for VisitTrack?

No. setHeadComponents in gatsby-ssr adds the tag to every page, which is all an analytics plugin would do for you.

Does VisitTrack track Gatsby Link navigation?

Yes. Gatsby's router uses the History API, which the tracker listens to, so every route change is a pageview without onRouteUpdate code.

Should I use the Gatsby Head API for analytics?

Not for a site-wide tag. The Head API is per page template, so it's easy to miss a template; gatsby-ssr's onRenderBody covers every page in one place.

Will VisitTrack affect Gatsby's performance?

The script is about 5 KB gzipped and deferred, so it doesn't block rendering or hydration. It also reports real-user Web Vitals from your visitors.

Can I use VisitTrack on a Gatsby site hosted on Netlify?

Yes. See the Netlify guide for deploy previews, headers and snippet injection.

Keep going

Add VisitTrack to your Gatsby site

Cookie-free analytics with revenue attribution, live visitors, funnels and session replays. One script tag, every feature on every plan. 14 days free, no card required.

Not on Gatsby? See all 29 integration guides.