All articles Guide

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.

We make Tidemark, so we are not a neutral party. We have tried to be fair, to say where other tools are the better choice, and to keep claims about other products to things you can check yourself.

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.

The snippet

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

Plain HTML

Paste the snippet inside <head> on each page, or in the shared include if you use one.

Astro

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.

Hugo

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.

Eleventy and Jekyll

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.

Next.js

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.

Hash-based routers

If your app navigates with #/route URLs, use the hash variant: script.hash.js.

Keep development visits out

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.

Test it

  1. Deploy, then open the live site in a private window.
  2. Watch current visitors in your dashboard.
  3. Navigate between pages and confirm each one appears.

If you run a content security policy, allow the analytics address in script-src and connect-src.

Next steps

You can also count outbound links, downloads and form submissions by adding variants to the file name. This guide has the details.

More from the blog