Google Tag Manager

Google Tag Manager wiring through Piko's built-in analytics frontend module, GTM container script loaded via WithFrontendModule(piko.ModuleAnalytics, ...), with automatic SPA navigation events pushed onto the dataLayer.

Overview

GTM moves tag management out of the codebase and into a hosted UI. Marketing teams add and remove pixels, conversion tags, and third-party scripts in the GTM dashboard without redeploying the application. Piko's analytics frontend module loads the container script (one HTTP request to www.googletagmanager.com), and pushes a structured page-view event onto the dataLayer on every client-side route change. GTM triggers then fire for client-routed apps.

The module forwards the rest of the Piko SPA lifecycle to the dataLayer with no glue code. It subscribes to route changes, server actions, modal opens, and errors, then pushes a structured event for each. The event names are piko_page_view, piko_navigation, piko_action, piko_modal_view, and piko_error. Build GTM triggers against those names.

The module ships with the core piko.sh/piko package, no separate go get. GTMContainerID is the GTM-prefixed container ID from the GTM admin UI. One AnalyticsConfig drives both transports. Set TrackingIDs alongside GTMContainerID to run GA4 from the same WithFrontendModule call. The module loads GTM, GA4, or both at runtime depending on which fields you populate. The typical pattern wires GA4 inside GTM and leaves TrackingIDs empty here. The loader script tags carry Subresource Integrity hashes and modulepreload, so Piko integrity-checks the tag-manager loader.

The main Piko-specific concern is CSP. GTM containers load scripts, pixels, and iframes that the application code never sees. The policy must allow the GTM and GA4 hosts plus a domain for every tag the container loads. Extend WithPikoDefaults() instead of building a policy from scratch. The defaults supply Piko's own script-src-attr hash for the async font loader, and a hand-rolled policy that omits it can break font loading. Browser console violations signal that a new tag needs a new domain added.

Configuration

import (
    "piko.sh/piko"
)

ssr := piko.New(
    piko.WithFrontendModule(piko.ModuleAnalytics, piko.AnalyticsConfig{
        GTMContainerID: "GTM-XXXXXXX", // required, GTM container ID
        DebugMode:      false,         // optional, console-log every dataLayer push
    }),
)

The analytics module is a built-in bundle served from a fixed /_piko/dist URL. It is not gated by run mode, so the GTM container loads and fires the same way in interpreted dev mode and in a compiled build. Set DebugMode to log every dataLayer push to the browser console.

Content Security Policy

GTM is more permissive than standalone GA4 because the container injects third-party code at runtime. The policy below starts from WithPikoDefaults() and adds the GTM and GA4 surface on top. WithPikoDefaults() already sets default-src, style-src with the Google Fonts host, script-src-attr with Piko's font-loader hash, font-src, img-src, and connect-src. The chained calls append to those directives. Expand ScriptSrc, ConnectSrc, and FrameSrc for every additional tag your container loads.

piko.WithCSP(func(b *piko.CSPBuilder) {
    b.WithPikoDefaults().
        ScriptSrc(
            piko.CSPSelf,
            piko.CSPHost("https://www.googletagmanager.com"),
            piko.CSPHost("https://tagmanager.google.com"),
        ).
        StyleSrc(
            piko.CSPHost("https://tagmanager.google.com"),
        ).
        ImgSrc(
            piko.CSPHTTPS,
        ).
        ConnectSrc(
            piko.CSPHost("https://www.google-analytics.com"),
            piko.CSPHost("https://*.google-analytics.com"),
            piko.CSPHost("https://*.analytics.google.com"),
            piko.CSPHost("https://*.googletagmanager.com"),
            piko.CSPHost("https://www.google.com"),
        ).
        FrameSrc(
            piko.CSPSelf,
            piko.CSPHost("https://www.googletagmanager.com"),
        )
}),

Note: every tag added in the GTM interface may require additional CSP domains. Watch the browser console for CSP violations and extend the policy as you onboard new tags.

Bootstrap

The frontend module is the bootstrap. WithFrontendModule(piko.ModuleAnalytics, ...) from the configuration block above is the registration call.

Tradeoffs

GTM is non-engineer-friendly by design. A marketer can ship a tracking pixel without a code review or a deploy. That is the point, and also the risk. Treat the GTM container as a deployment surface and audit changes the same way you audit code. Track who can publish container versions, review tags before publish, and tighten CSP as much as the loaded tags allow.

See also

Other analytics integrations:

Framework docs:

External: