Skip to content
All posts
AnalyticsAstroGuide

Add cookieless analytics to an Astro site

One inline tag in your base layout, what to do about view transitions, and how to fire events from an island — keeping Astro's near-zero JavaScript promise intact.

DA Orbit

Astro ships almost no JavaScript by default, so an analytics tag is often the only script on the page. Keep it that way: one inline tag in your base layout, cookieless, no consent banner.

The tag

Put it at the end of <body> in whatever layout every page shares. Use is:inline so Astro does not try to bundle or process it — you want the tag emitted exactly as written.

src/layouts/Base.astro

---
// src/layouts/Base.astro
---
<html lang="en">
  <head>
    <slot name="head" />
  </head>
  <body>
    <slot />
    <script
      is:inline
      src="${site.api}/tracker.js"
      data-site="YOUR_SITE_KEY"
    ></script>
  </body>
</html>

For a fully static Astro site with no client-side routing, that is the entire integration. Every page is a real document load and gets counted.

If you use view transitions

With <ClientRouter /> (formerly <ViewTransitions />) Astro swaps page content without a full reload, the same problem an SPA has. The tracker hooks pushState, which the router uses, so navigations are counted automatically. If you would rather be explicit, hook astro:page-load:

src/layouts/Base.astro

---
// With <ClientRouter /> (Astro's view transitions)
import { ClientRouter } from "astro:transitions";
---
<head>
  <ClientRouter />
</head>
<!-- The tracker hooks pushState, which astro:transitions uses,
     so navigations are counted. If you want to be explicit: -->
<script>
  document.addEventListener("astro:page-load", () => {
    window.quantalog?.("pageview");
  });
</script>

Do not add both the automatic and the manual call for the same navigation or you will double-count. Pick one.

Custom events

Astro islands and inline scripts both reach the same global. Guard it with optional chaining so a click before the tracker loads is a no-op.

src/components/Cta.astro

<button id="cta">Start free</button>
<script>
  document.getElementById("cta")?.addEventListener("click", () => {
    window.quantalog?.("cta_clicked");
  });
</script>

No consent banner

No cookies, no browser storage, nothing to disclose for the analytics tag. A banner is only back on the table if you add something that does set cookies. See do you need a cookie banner for analytics .

💡

The docs

the SEO audit tool

Try Quantalog on your own site

One script tag, no cookies, live numbers in about three seconds. Free forever on the Hobby plan.

Start free

Keep reading