First meaningful commit on the blog after the initial Vercel template clone. I wanted Google Analytics on the site, and I wanted it wired up the minimum-dependency way — no react-ga package, no wrappers, just the gtag script tag and a small helper module.
What landed
lib/ga.js— two functions.googleAnalyticsPageView(url)callsgtag('config', ID, { page_path: url })so SPA navigations are tracked.googleAnalyticsEvent({ action, params })is a wrapper aroundgtag('event', ...)for anything custom I want to fire later.// log the pageview with their URL export const googleAnalyticsPageView = (url) => { window.gtag('config', process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS, { page_path: url, }) } // log specific events happening. export const googleAnalyticsEvent = ({ action, params }) => { window.gtag('event', action, params) }pages/_document.js— a custom Next.jsDocumentthat injects thegtag.jsscript and the initialgtag('config', …)call into the<head>of every page.tsconfig.json— explicitly includes the newpages/_document.jsandlib/ga.jsso the TypeScript compiler doesn't miss them in the build. (This is the Next.js / TS interplay bit that I always forget: mixing.jsinto an otherwise-.tsxproject needs explicit includes.).env— deleted. It only heldIS_TEMPLATE=truefrom the template, which is no longer true.
The NEXT_PUBLIC_ env var
NEXT_PUBLIC_GOOGLE_ANALYTICS is the env var that holds the measurement ID. The NEXT_PUBLIC_ prefix is Next's mechanism for exposing env vars to the browser bundle — anything without that prefix stays server-side. For an analytics ID that's fine (it's literally designed to be visible in the page source), but it's worth noting: if the ID ever needed to be private, this is not where it would go.
Not yet working
This PR lands the plumbing but I haven't verified data is actually flowing to the GA dashboard yet. That's going to surface a small issue in a few PRs — namely that this is Universal Analytics, not GA4, and UA is about to be sunset. PR #3 migrates to GA4.