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 freeKeep reading
Add cookieless analytics to a Next.js app
One script tag for the App Router and the Pages Router, why route changes are the only catch, and how to fire custom events — no npm package, no consent banner.
Add cookieless analytics to a WordPress site
The three places to put the tag ranked by how well they survive updates, why page caching is not a problem, and how to track form submissions — no consent banner for the analytics itself.