Add Google Analytics (GA4)
Install GA4 into a codebase so it actually collects useful data the moment the site ships: the base tag on every page, correct pageviews across navigation (the part naive installs get wrong), and event tracking for the interactions that matter — without flooding the property with duplicates.
What "done" looks like
A teammate could deploy, open GA4 Realtime, click around the site, and see pageviews on every route plus named events for the important actions — with no duplicate or reserved-name events muddying the data, and the build still passing.
The one idea that makes this skill good: don't double-track
New GA4 web data streams have Enhanced Measurement on by default. With just the base tag installed, GA4 already auto-collects: page_view, scroll (at 90% depth), outbound link click, view_search_results (site search), video engagement, file_download, and form_start / form_submit.
So "track everything aggressively" does not mean firing gtag('event', 'scroll') or gtag('event', 'file_download') yourself. Re-emitting those reuses GA4's reserved/automatic event names and produces duplicate, conflicting data — which Google explicitly warns against. Aggressive-but-correct instrumentation means: let Enhanced Measurement handle its share, and add manual events only for what it misses:
- SPA / client-side route changes — the single most common broken thing. Enhanced Measurement's pageview can miss History API navigations, so SPAs under-report pageviews badly unless you wire route changes explicitly.
- Non-link clicks — buttons, CTAs,
role="button", JS-driven actions. Enhanced Measurement only catches outbound link clicks, not internal buttons/CTAs.
- Named conversions / recommended events —
generate_lead, sign_up, purchase, contact_form_submit, etc. These are never automatic; they carry the business value.
- Gaps in auto-tracking — file types outside GA4's default download list, non-YouTube video, AJAX forms that don't trip
form_submit.
When you instrument forms, prefer a meaningful named event (generate_lead, contact_form_submit) over re-firing raw form_submit, so the action is reportable as a conversion rather than colliding with the automatic event.
Before you touch any code
Get the Measurement ID. It looks like G-XXXXXXXXXX. The user supplies a fresh one at runtime — if it isn't in the request, ask for it before writing the tag. If they genuinely don't have it yet, fall back to an env var / placeholder and tell them clearly where to drop it in. Never invent an ID, and never ship a literal G-XXXXXXXXXX.
Every G-XXXXXXXXXX in this skill and its reference files is a placeholder for illustration only — not a default and not a real property. Before writing any file, substitute the exact ID the user gave you for this run into every spot the placeholder appears. After writing, grep the changed files for G-XXXXXXXXXX to confirm none of the placeholder slipped through.
Confirm scope in one line if unclear (e.g. "base tag + pageviews + click/CTA/form/conversion events across the whole app — sound right?"). Default to comprehensive since that's the usual intent here.
Workflow
Step 1 — Detect the stack
Look at the project root before assuming anything. Check package.json dependencies and lockfiles, config files, and directory layout:
next in deps, or next.config.*, or an app/ or pages/ dir → Next.js → read references/install-nextjs.md
react + a bundler (vite, react-scripts), index.html with a single <div id="root">, client-side router (react-router-dom) → React SPA → read references/install-react-spa.md
- Plain
.html files, no build step, or a static-site output → Static HTML → read references/install-static-html.md
- Something else (Vue, SvelteKit, Astro, Remix, Rails, plain server-rendered templates): apply the closest-matching reference's principles — base tag in the shared document head/layout, manual pageviews if routing is client-side, delegated event tracking — and say which pattern you adapted and why. The references teach the reasoning, not just the snippet.
State what you detected and which install path you're taking before editing, so the user can correct a wrong guess cheaply.
Step 2 — Check for an existing install (idempotency)
Grep the codebase first: gtag(, googletagmanager.com/gtag/js, dataLayer, G-, @next/third-parties, GoogleAnalytics, react-ga. If GA is already partly there:
- Same or no ID, partial install → complete/repair it; don't add a second tag. Two tags = doubled pageviews.
- A different ID → flag it and ask whether to replace, keep both (rare; intentional dual-tagging), or leave it. Don't silently clobber.
- GTM present → tell the user; adding gtag alongside GTM-managed GA double-counts. Usually the right move is to route GA4 through their existing GTM container instead — confirm before proceeding.
Step 3 — Install the base tag
Follow the chosen reference. The universal requirements regardless of stack:
- The tag loads on every page — put it in the shared layout/template/document head, never one page at a time.
- The Measurement ID lives in one place (a config constant or, preferably, an env var the framework exposes to the client:
NEXT_PUBLIC_* for Next, VITE_* for Vite). One source of truth.
- The GA script loads without blocking render (async, or the framework's script primitive).
Step 4 — Instrument events
Apply the dedup principle above. The mechanics, the full event catalog, GA4 naming rules, and the reusable auto-tracking helper live in references/events.md and assets/ga-autotrack.js — read the reference before writing event code.
The shape of a good install:
- A single tiny
trackEvent(name, params) wrapper so call sites never touch gtag directly (and so analytics is one easy thing to find/swap later).
- A declarative, delegated auto-tracker for clicks/CTAs driven by
data-ga-* attributes, mounted once — instead of hand-wiring an onClick to every button. assets/ga-autotrack.js is the vanilla version; adapt it to the stack.
- Explicit named events for conversions and any custom interaction the codebase reveals (search the code for forms, checkout/cart logic, signup flows, downloads, modals, video).
Read the actual components/pages to find what's worth tracking — don't just instrument a generic list. The valuable events are the ones specific to this product.
Step 5 — Verify it actually works
Don't hand back an install you haven't checked:
- Run the build / typecheck (
npm run build, tsc --noEmit, etc.). The install must not break the build.
- Grep to confirm the tag is in the shared layout and the ID resolves (env var defined, not an empty string).
- Confirm exactly one GA tag loads (no duplicate from a prior install).
- List every event you added with its trigger and parameters, so the user knows what to expect in GA4.
Step 6 — Report and hand off
Give the user:
- What changed — files touched, the tag location, where the ID is configured.
- Events added — table of event name, trigger, parameters, and whether it should be marked a Key Event (conversion) in GA4.
- Enhanced Measurement note — remind them which events come automatically so they understand the full picture and don't manually re-add them.
- A Realtime verification checklist they can run right after deploy — concrete: open GA4 → Reports → Realtime, do action X, expect event Y with parameter Z. Include the base pageview check plus one row per custom event.
- Key Event recommendations — which of the new events to flag as conversions in the GA4 UI (this can't be done from code), e.g.
generate_lead, sign_up, purchase.
Conventions (GA4)
- Event names: lowercase
snake_case, letters/numbers/underscores only — no spaces or hyphens. Names are case-sensitive.
- Never reuse GA4 reserved/automatic names for your own different-meaning events (
page_view, scroll, click, file_download, form_start, form_submit, session_start, first_visit, etc.). Use Google's recommended event names where one fits the action (generate_lead, sign_up, login, purchase, search, select_content, share) — matching them unlocks GA4's prebuilt reporting.
- Parameters: prefer informative, low-cardinality values (
button_text, link_url, page_path, page_title, section, form_id, item_id). Avoid putting PII (emails, names, raw query strings) into parameters.
Reference files
references/install-nextjs.md — App Router & Pages Router; the official @next/third-parties GoogleAnalytics component (handles route-change pageviews for you), env-var setup, and where manual events still belong.
references/install-react-spa.md — base tag in index.html, manual SPA pageview tracking on router changes (the part that's easy to get wrong), and the trackEvent util.
references/install-static-html.md — the gtag snippet, keeping it DRY across multiple HTML files / includes, and wiring the auto-tracker.
references/events.md — Enhanced Measurement vs. manual responsibilities, the recommended-events catalog, naming rules, the data-ga-* pattern, and ready-to-paste event code.
assets/ga-autotrack.js — drop-in vanilla delegated click/CTA tracker plus the trackEvent wrapper.
1---2name: add-google-analytics3description: Install Google Analytics 4 (GA4) into a website's codebase — the base gtag tag plus event tracking — wiring it into the real framework so pageviews fire on every route and meaningful interactions are tracked. Use this whenever someone wants to add, install, set up, wire up, or instrument Google Analytics / GA4 / gtag / a measurement ID (G-XXXXXXXXXX) on their site, or asks to "track events", "add analytics", "set up conversion tracking", or "get analytics working before launch" — even if they don't name GA4 specifically and even if they just hand you a measurement ID and a repo. Auto-detects the stack (static HTML, React SPA, Next.js, and similar) and writes the install in the idiomatic way for that stack. This skill writes code into the project; it is not an audit-only report.4---56# Add Google Analytics (GA4)78Install GA4 into a codebase so it actually collects useful data the moment the site ships: the base tag on every page, correct pageviews across navigation (the part naive installs get wrong), and event tracking for the interactions that matter — without flooding the property with duplicates.910## What "done" looks like1112A teammate could deploy, open GA4 Realtime, click around the site, and see pageviews on every route plus named events for the important actions — with no duplicate or reserved-name events muddying the data, and the build still passing.1314## The one idea that makes this skill good: don't double-track1516New GA4 web data streams have **Enhanced Measurement on by default**. With just the base tag installed, GA4 *already* auto-collects: `page_view`, `scroll` (at 90% depth), outbound link `click`, `view_search_results` (site search), video engagement, `file_download`, and `form_start` / `form_submit`.1718So "track everything aggressively" does **not** mean firing `gtag('event', 'scroll')` or `gtag('event', 'file_download')` yourself. Re-emitting those reuses GA4's reserved/automatic event names and produces duplicate, conflicting data — which Google explicitly warns against. Aggressive-but-correct instrumentation means: let Enhanced Measurement handle its share, and **add manual events only for what it misses**:1920- **SPA / client-side route changes** — the single most common broken thing. Enhanced Measurement's pageview can miss History API navigations, so SPAs under-report pageviews badly unless you wire route changes explicitly.21- **Non-link clicks** — buttons, CTAs, `role="button"`, JS-driven actions. Enhanced Measurement only catches *outbound link* clicks, not internal buttons/CTAs.22- **Named conversions / recommended events** — `generate_lead`, `sign_up`, `purchase`, `contact_form_submit`, etc. These are never automatic; they carry the business value.23- **Gaps in auto-tracking** — file types outside GA4's default download list, non-YouTube video, AJAX forms that don't trip `form_submit`.2425When you instrument forms, prefer a meaningful named event (`generate_lead`, `contact_form_submit`) over re-firing raw `form_submit`, so the action is reportable as a conversion rather than colliding with the automatic event.2627## Before you touch any code28291. **Get the Measurement ID.** It looks like `G-XXXXXXXXXX`. The user supplies a fresh one at runtime — if it isn't in the request, ask for it before writing the tag. If they genuinely don't have it yet, fall back to an env var / placeholder and tell them clearly where to drop it in. Never invent an ID, and never ship a literal `G-XXXXXXXXXX`.3031 **Every `G-XXXXXXXXXX` in this skill and its reference files is a placeholder for illustration only — not a default and not a real property.** Before writing any file, substitute the exact ID the user gave you for this run into every spot the placeholder appears. After writing, grep the changed files for `G-XXXXXXXXXX` to confirm none of the placeholder slipped through.322. **Confirm scope in one line** if unclear (e.g. "base tag + pageviews + click/CTA/form/conversion events across the whole app — sound right?"). Default to comprehensive since that's the usual intent here.3334## Workflow3536### Step 1 — Detect the stack3738Look at the project root before assuming anything. Check `package.json` dependencies and lockfiles, config files, and directory layout:3940- `next` in deps, or `next.config.*`, or an `app/` or `pages/` dir → **Next.js** → read `references/install-nextjs.md`41- `react` + a bundler (`vite`, `react-scripts`), `index.html` with a single `<div id="root">`, client-side router (`react-router-dom`) → **React SPA** → read `references/install-react-spa.md`42- Plain `.html` files, no build step, or a static-site output → **Static HTML** → read `references/install-static-html.md`43- Something else (Vue, SvelteKit, Astro, Remix, Rails, plain server-rendered templates): apply the closest-matching reference's *principles* — base tag in the shared document head/layout, manual pageviews if routing is client-side, delegated event tracking — and say which pattern you adapted and why. The references teach the reasoning, not just the snippet.4445State what you detected and which install path you're taking before editing, so the user can correct a wrong guess cheaply.4647### Step 2 — Check for an existing install (idempotency)4849Grep the codebase first: `gtag(`, `googletagmanager.com/gtag/js`, `dataLayer`, `G-`, `@next/third-parties`, `GoogleAnalytics`, `react-ga`. If GA is already partly there:5051- **Same or no ID, partial install** → complete/repair it; don't add a second tag. Two tags = doubled pageviews.52- **A different ID** → flag it and ask whether to replace, keep both (rare; intentional dual-tagging), or leave it. Don't silently clobber.53- **GTM present** → tell the user; adding gtag alongside GTM-managed GA double-counts. Usually the right move is to route GA4 through their existing GTM container instead — confirm before proceeding.5455### Step 3 — Install the base tag5657Follow the chosen reference. The universal requirements regardless of stack:5859- The tag loads on **every** page — put it in the shared layout/template/document head, never one page at a time.60- The Measurement ID lives in **one** place (a config constant or, preferably, an env var the framework exposes to the client: `NEXT_PUBLIC_*` for Next, `VITE_*` for Vite). One source of truth.61- The GA script loads without blocking render (async, or the framework's script primitive).6263### Step 4 — Instrument events6465Apply the dedup principle above. The mechanics, the full event catalog, GA4 naming rules, and the reusable auto-tracking helper live in `references/events.md` and `assets/ga-autotrack.js` — read the reference before writing event code.6667The shape of a good install:68- A single tiny `trackEvent(name, params)` wrapper so call sites never touch `gtag` directly (and so analytics is one easy thing to find/swap later).69- A declarative, delegated auto-tracker for clicks/CTAs driven by `data-ga-*` attributes, mounted once — instead of hand-wiring an `onClick` to every button. `assets/ga-autotrack.js` is the vanilla version; adapt it to the stack.70- Explicit named events for conversions and any custom interaction the codebase reveals (search the code for forms, checkout/cart logic, signup flows, downloads, modals, video).7172Read the actual components/pages to find what's worth tracking — don't just instrument a generic list. The valuable events are the ones specific to *this* product.7374### Step 5 — Verify it actually works7576Don't hand back an install you haven't checked:7778- Run the build / typecheck (`npm run build`, `tsc --noEmit`, etc.). The install must not break the build.79- Grep to confirm the tag is in the shared layout and the ID resolves (env var defined, not an empty string).80- Confirm exactly **one** GA tag loads (no duplicate from a prior install).81- List every event you added with its trigger and parameters, so the user knows what to expect in GA4.8283### Step 6 — Report and hand off8485Give the user:861. **What changed** — files touched, the tag location, where the ID is configured.872. **Events added** — table of event name, trigger, parameters, and whether it should be marked a Key Event (conversion) in GA4.883. **Enhanced Measurement note** — remind them which events come automatically so they understand the full picture and don't manually re-add them.894. **A Realtime verification checklist** they can run right after deploy — concrete: open GA4 → Reports → Realtime, do action X, expect event Y with parameter Z. Include the base pageview check plus one row per custom event.905. **Key Event recommendations** — which of the new events to flag as conversions in the GA4 UI (this can't be done from code), e.g. `generate_lead`, `sign_up`, `purchase`.9192## Conventions (GA4)9394- Event names: lowercase `snake_case`, letters/numbers/underscores only — no spaces or hyphens. Names are case-sensitive.95- Never reuse GA4 reserved/automatic names for your own different-meaning events (`page_view`, `scroll`, `click`, `file_download`, `form_start`, `form_submit`, `session_start`, `first_visit`, etc.). Use Google's **recommended** event names where one fits the action (`generate_lead`, `sign_up`, `login`, `purchase`, `search`, `select_content`, `share`) — matching them unlocks GA4's prebuilt reporting.96- Parameters: prefer informative, low-cardinality values (`button_text`, `link_url`, `page_path`, `page_title`, `section`, `form_id`, `item_id`). Avoid putting PII (emails, names, raw query strings) into parameters.9798## Reference files99100- `references/install-nextjs.md` — App Router & Pages Router; the official `@next/third-parties` `GoogleAnalytics` component (handles route-change pageviews for you), env-var setup, and where manual events still belong.101- `references/install-react-spa.md` — base tag in `index.html`, manual SPA pageview tracking on router changes (the part that's easy to get wrong), and the `trackEvent` util.102- `references/install-static-html.md` — the gtag snippet, keeping it DRY across multiple HTML files / includes, and wiring the auto-tracker.103- `references/events.md` — Enhanced Measurement vs. manual responsibilities, the recommended-events catalog, naming rules, the `data-ga-*` pattern, and ready-to-paste event code.104- `assets/ga-autotrack.js` — drop-in vanilla delegated click/CTA tracker plus the `trackEvent` wrapper.