feat: add optional GA4 analytics gated behind NEXT_PUBLIC_GA_ID - #9
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 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".
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>
iblh
force-pushed
the
feat/google-analytics
branch
from
September 13, 2026 00:21
347bd2f to
fc78631
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Google Analytics 4 to uplotr, gated behind a
NEXT_PUBLIC_GA_IDenvironment variable so it is off by default.The landing page advertises "no product analytics by default" (app/page.tsx:30). An unconditional
gtagwould 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) — returnsnullunlessNEXT_PUBLIC_GA_IDis set; loads gtag withstrategy="afterInteractive"and enablesanonymize_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— cleanpnpm typecheck— cleanpnpm test— 25 passed, 6 skippedNEXT_PUBLIC_GA_IDunset → grepped prerendered HTML, zerogoogletagmanagerreferencesNEXT_PUBLIC_GA_IDset → gtag script present with the expected IDNEXT_PUBLIC_GA_IDin Vercel Production only (leave Preview/Development unset so test deploys don't pollute data)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