How to Use Umami with Astro: Complete Guide
Step-by-step guide to integrating Umami with your Astro website.
Umami is an open-source, privacy-focused web analytics tool that gives you the data you actually need without the bloat of Google Analytics. No cookies, no personal data collection, and full GDPR compliance out of the box. You can self-host it for free or use their cloud service, which has a free Hobby tier and a paid Pro plan at $20 per month for 1 million events (check umami.is/pricing for current numbers).
For Astro sites, Umami is a particularly good fit. The tracking script is tiny, roughly 4.6KB minified and about 2.2KB gzipped over the wire (measured against the Umami Cloud script on 2026-05-30), so it does not slow down your pages, and you do not need a cookie consent banner. Setup takes about five minutes.
Verified against the Umami docs and Astro docs on 2026-05-29. At the time of writing Astro is on 6.4.2 and the @astrojs/node adapter is on 10.1.2.
Prerequisites
- Node.js 18.18 or newer for Umami's self-host install. Note that Astro 6 itself requires a much newer Node, v22.12.0 or higher, since Astro 6 dropped Node 18 and Node 20 support entirely. So if you run both on the same machine, Node 22.12+ covers both.
- An Astro project (
npm create astro@latest) - An Umami instance (self-hosted or Umami Cloud at umami.is)
Installation
Umami does not require a client npm package. The tracker works through a single script tag, so nothing extra ends up in your Astro bundle. The only npm package named umami is the self-hosted server application itself, not something you install into your Astro site. But first, you need a running Umami instance.
Option 1: Umami Cloud (easiest)
Sign up at umami.is, create a website, and copy the tracking code from the Tracking code section of the dashboard. The Cloud tracker script is served from https://cloud.umami.is/script.js.
Option 2: Self-hosted with Docker (free)
Umami ships a docker-compose.yml containing the app plus a PostgreSQL database. Per the install docs, self-hosting needs Node.js 18.18+ and PostgreSQL v12.14 or newer.
git clone https://github.com/umami-software/umami.git
cd umami
docker compose up -d
This starts Umami on http://localhost:3000 with a PostgreSQL database. The default login is username admin and password umami. Change that password immediately after the first login, as the docs warn.
Once Umami is running, add a new website in the dashboard and copy the website ID.
Configuration
Add your Umami configuration to .env. In Astro, only variables prefixed with PUBLIC_ are exposed to client-side code, so the website ID and script source both need that prefix to be readable from a <script> tag in the browser:
PUBLIC_UMAMI_WEBSITE_ID=your_website_id
PUBLIC_UMAMI_SRC=https://your-umami-instance.com/script.js
If you are using Umami Cloud, the script source is https://cloud.umami.is/script.js (verified returning a 200 with content-type: application/javascript on 2026-05-29).
If your script is hosted on a different origin than the one Umami should report data to, set data-host-url on the script tag to point at your Umami collect endpoint. The tracker otherwise infers the host from the script's own URL.
Basic Usage
Add the Umami tracking script to your base layout. The cleanest approach is to include it in the <head> of your layout component:
---
// src/layouts/BaseLayout.astro
export interface Props {
title: string;
description?: string;
}
const { title, description } = Astro.props;
const umamiId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID;
const umamiSrc = import.meta.env.PUBLIC_UMAMI_SRC;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
{description && <meta name="description" content={description} />}
{umamiId && umamiSrc && (
<script
defer
src={umamiSrc}
data-website-id={umamiId}
/>
)}
</head>
<body>
<slot />
</body>
</html>
That is it for basic pageview tracking. Every page that uses this layout will be tracked automatically.
For custom event tracking, use Umami's JavaScript API. This is useful for tracking button clicks, form submissions, or other interactions:
---
// src/components/NewsletterForm.astro
---
<form id="newsletter-form">
<input type="email" placeholder="Your email" required />
<button type="submit">Subscribe</button>
</form>
<script>
const form = document.getElementById("newsletter-form");
form?.addEventListener("submit", () => {
// Track the event in Umami. Guard via window.umami so the page
// does not throw if the tracker has not loaded yet (or is blocked).
if (typeof window.umami !== "undefined") {
window.umami.track("newsletter-signup");
}
});
</script>
The tracker exposes a global umami object. The Umami docs list these call signatures: umami.track() (manual page view), umami.track(event_name), umami.track(event_name, data), and the newer umami.identify(unique_id, data) for attaching session data. Guarding with typeof window.umami !== "undefined" is the documented-equivalent safe pattern, and on iOS Safari accessing the bare umami identifier instead of window.umami can throw if the global is not yet defined.
You can also track events with additional data:
window.umami.track("button-click", { label: "pricing-cta", position: "hero" });
Production Tips
Disable tracking in development. Add a check so the script only loads in production. You can use
import.meta.env.PRODin Astro to conditionally render the script tag, keeping your dev analytics clean.Self-host for zero cost. Umami runs well on minimal hardware. A $5/month VPS handles millions of pageviews. Deploy it alongside your other services using Docker Compose.
Use the Umami API for custom dashboards. Umami exposes a REST API for querying your analytics data. You could build a custom stats page on your Astro site that pulls data directly from Umami. Because that page needs a live API call per request, mark it for on-demand rendering with
export const prerender = falseand add a server adapter (see the note below); the rest of your blog stays static.Track outbound links and add custom event data. Add
data-umami-event="outbound-link"to an element to fire a named event on click, and adddata-umami-event-*attributes (for exampledata-umami-event-position="footer") to attach extra data to that event. This helps measure affiliate performance and content effectiveness.
Set up multiple websites. If you run several Astro sites, a single Umami instance can track all of them. Create a separate website entry for each domain in the Umami dashboard and use different website IDs. If one script serves multiple domains, you can restrict tracking with data-domains on the script tag.
A Note on Astro Rendering Modes
The basic Umami setup needs nothing special from Astro because a <script> tag in your layout's <head> ships with the static HTML regardless of rendering mode. Astro 6 prerenders your entire site to static HTML by default, which is exactly what you want for a tracked blog.
One thing to know if you read older tutorials: output: 'hybrid' was removed in Astro 5. There is no hybrid value anymore. The valid output values are 'static' (the default) and 'server'. To render specific pages on demand, for example a stats page that calls the Umami API, keep the default static output and add export const prerender = false to just that page or endpoint, plus a server adapter such as @astrojs/node:
npx astro add node
Everything else on the site stays prerendered and the Umami script keeps working unchanged.
Common Errors and Fixes
No data appears in the dashboard. The single most common cause is a domain mismatch. By default Umami only collects events from the domain configured for that website ID. While testing on localhost, either add localhost as an allowed domain for the website, or expect no events until you deploy. Also confirm the script actually loaded (check the Network tab for script.js returning 200) and that the data-website-id matches the ID in your dashboard.
umami is not defined thrown in the console. This happens when code calls umami.track(...) before the deferred tracker script has finished loading, or when an ad blocker has blocked it entirely. Always guard with typeof window.umami !== "undefined" before calling, and reference window.umami rather than the bare umami identifier so it does not throw on browsers like iOS Safari where the global may be absent.
Environment variable is undefined in the browser. Astro only exposes variables prefixed with PUBLIC_ to client-side code. If import.meta.env.PUBLIC_UMAMI_WEBSITE_ID is empty in the rendered HTML, check the prefix and restart the dev server, since env changes are read at startup.
Events fire in development and pollute real stats. Gate the script with import.meta.env.PROD (a built-in Astro flag that is true only in production builds) so the tracker never loads during astro dev.
Self-host install fails on an old Node or Postgres. The Umami install docs require Node.js 18.18+ and PostgreSQL v12.14 or newer. Older versions are a frequent cause of failed docker compose up or migration errors.
Official Docs and Examples
- Umami install and self-hosting guide: https://docs.umami.is/docs/install
- Umami tracker script configuration (all data attributes): https://docs.umami.is/docs/tracker-configuration
- Umami JavaScript tracker functions (
umami.track,umami.identify): https://docs.umami.is/docs/tracker-functions - Umami source and Docker compose example repo: https://github.com/umami-software/umami
- Astro on-demand rendering and adapters: https://docs.astro.build/en/guides/on-demand-rendering/
- Astro environment variables (
PUBLIC_prefix,import.meta.env.PROD): https://docs.astro.build/en/guides/environment-variables/
Alternatives to Consider
- Plausible if you want a hosted privacy-focused analytics service with a slightly more polished dashboard and email reports built in.
- Fathom if you need a privacy-first tool with advanced features like custom domains for the tracking script and EU isolation.
- Google Analytics if you need detailed user behavior data, conversion funnels, and integration with Google Ads. The tradeoff is a heavier script and the need for cookie consent.
Wrapping Up
Umami gives you the analytics you need without the privacy baggage. For Astro sites, the integration is a single script tag. No client npm packages, no build plugins, no configuration complexity. You get pageviews, referrers, device stats, and custom events in a clean dashboard. If you care about your visitors' privacy and want an analytics tool that respects it, Umami is the best open-source option available.
Sources
All versions and facts below were checked on 2026-05-29.
- Astro latest version (6.4.2), npm registry: https://registry.npmjs.org/astro/latest
@astrojs/nodelatest version (10.1.2), npm registry: https://registry.npmjs.org/@astrojs/node/latestumaminpm package (2.10.0, the self-hosted server), npm registry: https://registry.npmjs.org/umami/latest- Umami Cloud pricing (free Hobby tier, Pro at $20/month for 1M events): https://umami.is/pricing
- Umami install requirements (Node 18.18+, PostgreSQL v12.14+, default admin/umami, Docker compose): https://docs.umami.is/docs/install
- Umami tracker configuration and data attributes: https://docs.umami.is/docs/tracker-configuration
- Umami tracker functions (
umami.track,umami.identifysignatures): https://docs.umami.is/docs/tracker-functions - Umami source repository: https://github.com/umami-software/umami
- Umami Cloud script URL returning 200 and its size (
https://cloud.umami.is/script.js, ~4.6KB minified / ~2.2KB gzipped), verified viacurl -sIandcurl -s | gzip -c | wc -con 2026-05-30 - Astro 6 Node.js requirement, v22.12.0 or higher, with Node 18 and Node 20 support dropped: https://docs.astro.build/en/guides/upgrade-to/v6/
- Astro on-demand rendering (static default,
hybridremoved in Astro 5,output: 'server',export const prerender, adapters): https://docs.astro.build/en/guides/on-demand-rendering/ - Astro environment variables (
PUBLIC_prefix,import.meta.env.PROD): https://docs.astro.build/en/guides/environment-variables/
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.