Frameworks · 2 min setup

How to add analytics to an Astro site

To add VisitTrack to Astro, put the script tag with is:inline in the <head> of the layout every page uses (usually src/layouts/Layout.astro). Static pages are counted on load, and with <ClientRouter /> view transitions each soft navigation is counted too.

Updated

Astro processes and bundles <script> tags by default. An analytics tag must reach the browser exactly as written — external src, defer, and the data-site attribute the tracker reads — so mark it is:inline. (Astro already treats any script with attributes besides src as inline; writing it explicitly just makes the intent obvious to the next person.)

Astro at a glance

Where the tag goes
<head> of your base layout, e.g. src/layouts/Layout.astro
Directive
is:inline so Astro leaves the tag untouched
View transitions
<ClientRouter /> navigations tracked (History API)
Output modes
Static, server and hybrid — and any adapter
Content collections / MDX
No changes needed; they render inside the layout

How to install VisitTrack on Astro

  1. Step 1: Copy your site id

    Add the site in VisitTrack and copy the snippet from the install step (or Settings → General). Replace SITE_ID below.

  2. Step 2: Add the tag to your base layout

    Open the layout that renders <html> and <head> for every page. If you have several layouts, put it in the one they all extend — or in a shared BaseHead.astro component used by each.

    src/layouts/Layout.astro
    ---
    const { title } = Astro.props;
    ---
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>{title}</title>
        <script is:inline defer data-site="SITE_ID" src="https://visitrack.app/tracker.js"></script>
      </head>
      <body>
        <slot />
      </body>
    </html>
  3. Step 3: Using view transitions? Keep the tag in the head only

    With <ClientRouter />, Astro swaps the page in the browser and compares the old and new <head>. Because every page renders the identical tag from the same layout, it's kept as it is rather than run again. Don't add data-astro-rerun to it — that forces a re-run on every navigation and would double-count.

  4. Step 4: Build, deploy and open the site

    astro dev serves on localhost, which the tracker ignores. Deploy (or run astro preview with data-allow-local temporarily) and browse a few pages.

How to check VisitTrack is working on Astro

  1. View source on a deployed page: the tag appears in <head> exactly as written, not rewritten into a bundled module.
  2. Open two or three pages; the VisitTrack live view shows you, and the Pages tab lists each path.
  3. With <ClientRouter />, DevTools → Network should show exactly one POST to visitrack.app/api/collect per navigation, not two.

Without view transitions, each page is a full page load and gets its pageview on load. With <ClientRouter />, Astro's router updates the URL with history.pushState (or replaceState for data-astro-history="replace" links), which the tracker wraps — so soft navigations are pageviews too. You don't need an astro:page-load listener for VisitTrack.

Pageviews are keyed on the pathname, so a link that only changes the #hash or the query string isn't counted again.

How to track custom events and goals in Astro

On a mostly static site, the attribute-based events do most of the work: data-vt-goal="newsletter_cta" records a click on any element, and data-vt-scroll="pricing_viewed" fires once when that element is at least half visible. Both are re-scanned after client-side navigations.

For logic, call window.visitrack() from a client script. In a regular (bundled) Astro <script>, it runs once per page load; with view transitions, put per-page code in an astro:page-load listener.

src/components/Newsletter.astro
<a href="/pricing" data-vt-goal="pricing_cta">See pricing</a>

<form id="newsletter">…</form>
<script>
  document.addEventListener("astro:page-load", () => {
    const form = document.getElementById("newsletter");
    form?.addEventListener("submit", () => window.visitrack?.("newsletter_signup"));
  });
</script>

Automatic events are on by default too: file downloads, mailto:/tel: clicks, form submits, rage clicks, JavaScript errors and 404 pages (Astro's 404.astro title usually contains "404" or "not found", which is what the tracker checks).

Script options you might need on Astro

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).

Astro troubleshooting

The tag shows up as a bundled type="module" script

Astro processed it. Add is:inline and make sure the tag has its data-site attribute.

Double pageviews with view transitions

Look for a second copy of the tag in a page or component body, or a data-astro-rerun attribute. Keep exactly one tag, in the layout's <head>.

Some pages aren't tracked, or counts jump after visiting them

Those pages use a different layout without the tag. With view transitions, navigating from such a page back to one with the tag runs the script a second time. Move the tag into a shared head component that every layout includes.

CSP blocks the script

Add https://visitrack.app to script-src and connect-src wherever your policy is set (host headers, middleware or a meta tag).

Astro analytics FAQ

Why does the VisitTrack script need is:inline in Astro?

Without it, Astro may treat the script as one to process and bundle. The tracker reads its configuration (data-site and options) from its own script tag and must load from visitrack.app, so it has to reach the browser unchanged.

Does VisitTrack work with Astro view transitions (ClientRouter)?

Yes. The router uses the History API, which the tracker listens to, so each soft navigation is recorded as a pageview with no extra code. Keep the tag in the shared head and don't mark it data-astro-rerun.

Is there an Astro integration package for VisitTrack?

No; a single tag in the layout is the whole install. That keeps it working across Astro upgrades and with any adapter.

Does VisitTrack work with Astro's static output on GitHub Pages or Netlify?

Yes. The tag is plain HTML in every generated page, so it works on any static host.

Will it affect my Astro site's Lighthouse score?

The script is about 5 KB gzipped and loads with defer, so its impact should be minimal. VisitTrack also collects real-user Web Vitals, so you can see field data rather than lab scores.

Keep going

Add VisitTrack to your Astro 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 Astro? See all 29 integration guides.