Skip to content

feat: add optional GA4 analytics gated behind NEXT_PUBLIC_GA_ID - #9

Merged
iblh merged 2 commits into
mainfrom
feat/google-analytics
Sep 13, 2026
Merged

iblh merged 2 commits into
mainfrom
feat/google-analytics

Conversation

@iblh

@iblh iblh commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Google Analytics 4 to uplotr, gated behind a NEXT_PUBLIC_GA_ID environment variable so it is off by default.

The landing page advertises "no product analytics by default" (app/page.tsx:30). An unconditional gtag would make that claim false for every self-hoster and would silently ship their users' data to the official GA property. Gating on an env var keeps that promise literally true while letting only the official uplotr.com deployment opt in.

Changes

  • components/analytics.tsx (new) — returns null unless NEXT_PUBLIC_GA_ID is set; loads gtag with strategy="afterInteractive" and enables anonymize_ip.
  • app/layout.tsx — renders <Analytics /> in the root layout.
  • .env.example — documents the variable, commented out. The real measurement ID is deliberately not committed; it is set only in Vercel Production.

Test plan

Verified locally before pushing:

  • pnpm lint — clean
  • pnpm typecheck — clean
  • pnpm test — 25 passed, 6 skipped
  • Built with NEXT_PUBLIC_GA_ID unset → grepped prerendered HTML, zero googletagmanager references
  • Built with NEXT_PUBLIC_GA_ID set → gtag script present with the expected ID
  • After merge: set NEXT_PUBLIC_GA_ID in Vercel Production only (leave Preview/Development unset so test deploys don't pollute data)
  • After deploy: confirm the tag fires in GA4 Realtime

Notes

NEXT_PUBLIC_* values are inlined into the client bundle, so the measurement ID will be publicly visible on uplotr.com. That is normal and unavoidable for GA4 — it is a write-only collection ID, not a secret — but it is why the value belongs in the Vercel dashboard rather than in this open-source repo, where self-hosters would otherwise inherit it.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Sep 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
uplotr Ready Ready Preview Sep 13, 2026 12:43am UTC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 347bd2f580

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/analytics.tsx
Adds a Google Analytics component that renders nothing unless
NEXT_PUBLIC_GA_ID is set. This keeps the landing page's "no product
analytics by default" promise true for self-hosted instances, while
allowing the official uplotr.com deployment to opt in via env var.

IP anonymization is enabled on the gtag config.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GA4 writes _ga cookies as soon as gtag is configured, which GDPR and
ePrivacy treat as storage requiring prior opt-in. The previous version
loaded gtag for every visitor on every route as soon as NEXT_PUBLIC_GA_ID
was set, so visitors who had not consented were tracked regardless.

Analytics now renders nothing until an explicit opt-in is recorded:

- lib/consent.ts holds the consent state. Storage failures and unknown
  values read as "no decision" rather than as consent, so a broken or
  blocked localStorage can never be mistaken for an opt-in.
- components/consent-banner.tsx asks once and offers Decline as a real
  button, not a dismiss affordance.
- Both components start hidden and read storage in an effect, keeping
  server and client markup identical on first paint.

Verified by building with NEXT_PUBLIC_GA_ID set: the rendered HTML still
contains zero googletagmanager references before consent is given.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@iblh
iblh merged commit 1861e3b into main Sep 13, 2026
4 checks passed
@iblh
iblh deleted the feat/google-analytics branch September 13, 2026 01:31
@iblh iblh mentioned this pull request Sep 13, 2026
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant