Google Analytics Integration
Web analytics are opt-in and minimal: supply a Google Analytics or Tag Manager ID in Settings → Integrations, and every built page gets the matching Google script — nothing more, and nothing at all until your visitor has interacted with the page. There is no per-page analytics, no data flowing into the CMS, and (in air-gap mode) no external calls whatsoever.
Which IDs to set
Two fields, coordinated:
- GA4 measurement ID (
G-XXXXXXXXXX) — the direct Google Analytics 4 route: the standard gtag.js is added to every page. - GTM container ID (
GTM-XXXXXXX) — instead routes the site through Google Tag Manager, where the tags you manage live in the container.
They work on the same page but don’t both load — GTM wins when both are filled (the GA4 gtag loader is intentionally skipped so tracking isn’t duplicated through two paths). Pick one: the GA4 ID if you track in Google Analytics directly, or GTM if the container is where your tracking lives.
How the script gets on the page
Every build injects the loader snippet into every built page once, and the loader itself is deliberately polite:
- It fires only after the visitor interacts — click, scroll, keydown, or the first touch.
- If your site’s cookie notice is active, the analytics only run after the visitor has accepted the cookie — the cookie is used directly, and refusals (
cookie_consent=0) are honored by the loader even without tooling. - GA4/GTM configured: exactly one of the two loads — GA4 alone loads gtag.js; GTM loads the standard GTM loader (full container control), with this so the site can’t double-download tracking.
The tags appear at build time in the generated pages, so the effect is instant per page rebuild and no dynamic configuration is needed.
Air-gap mode
Setting the environment’s air-gap mode switches the whole panel off:
- The two fields become read-only and an inline notice says so: “External analytics scripts (GA4, GTM) are disabled and will not be emitted in builds.”
- The build itself strips the IDs, so a stale value cannot make the site phone Google — the also-verified snippet scrubbing keeps out-of-site URLs out of the emitted pages too.
- Enabling air-gap later automatically disarms these IDs; the values stay in settings for later.
Related field on the same tab
- X / Twitter handle (with the
@) — not analytics, builds thetwitter:sitemeta tag on every built page for nicer link cards. Same Integrations panel, different system, listed for completeness.
Practical notes
- Leave the fields blank to have no analytics at all — the CMS serves clean, scriptless pages, which is the project’s by-default behavior.
- Because these are text fields without client-side validation, a paste that misses a character (e.g. forgetting the
G-prefix) results in an unused script rather than an error — check the page source of a built page to confirm the measurement ID appears correctly. - If you use GTM, remember that GA4 tags you create inside the container are managed at the provider — leave
ga4_idblank in Chinaski to avoid a second, double-head path. - Custom trackers beyond GA4/GTM (Piwik, Plausible, Meta pixel…) belong in the Snippets system — the standard head/body injection mechanism — with the same consent-aware loading and (in air-gap mode) external-URL stripping rules.
The short version
Paste a GA4 or a GTM identifier under Settings → Integrations and the corresponding Google loader is baked into every build — consent-aware if your cookie banner is on, only after a first interaction, doubly secured in air-gap mode — and nothing about it round-trips through your server.