How to add cookie-free analytics to Astro, Hugo, Next.js and other static sites
One script tag is all it takes. Where to put it in Astro, Hugo, Next.js, Eleventy and plain HTML, how single-page apps are handled and how to test it.
One script tag is all it takes. Where to put it in Astro, Hugo, Next.js, Eleventy and plain HTML, how single-page apps are handled and how to test it.
Static sites and frameworks make analytics easy, because everything shares a layout. You add one script tag to the shared <head> and every page is covered. This guide shows where that goes in the common setups. The examples use Tidemark, whose script follows Plausible’s conventions, so they work for other Plausible-style tools too.
<script defer data-domain="yourdomain.com" src="https://app.tidemarkanalytics.com/js/script.js"></script>
Replace the domain with your site’s, and the src with your own Tidemark address if you self-host. The script is about 2 KB, sets no cookies, and sends a pageview on load and on every history change.
Paste the snippet inside <head> on each page, or in the shared include if you use one.
Put it in your base layout, for example src/layouts/Base.astro:
<head>
<script is:inline defer data-domain="yourdomain.com" src="https://app.tidemarkanalytics.com/js/script.js"></script>
</head>
is:inline stops Astro from bundling or rewriting the tag. Astro’s view transitions change the page without a full reload, and the script’s history tracking picks that up.
Add it to your theme’s layouts/partials/head.html (or a partial the head already includes):
<script defer data-domain="{{ .Site.Params.analyticsDomain }}" src="https://app.tidemarkanalytics.com/js/script.js"></script>
Setting the domain once in hugo.toml keeps staging builds from reporting under the wrong site.
Add the tag to the base layout’s <head>. In Jekyll you can wrap it in {% if jekyll.environment == "production" %} so local builds are not counted.
In the App Router, add it to app/layout.tsx with the built-in script component:
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script defer data-domain="yourdomain.com" src="https://app.tidemarkanalytics.com/js/script.js" strategy="afterInteractive" />
</body>
</html>
);
}
Client-side navigation is a history change, so every route gets a pageview without extra code.
If your app navigates with #/route URLs, use the hash variant: script.hash.js.
The script ignores localhost by default, so running your site locally does not add visits. Add the local variant only if you want to test there.
If you run a content security policy, allow the analytics address in script-src and connect-src.
You can also count outbound links, downloads and form submissions by adding variants to the file name. This guide has the details.