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
