Wire up Google Analytics 4 and tool_engaged conversion event#114
Merged
Conversation
This app has been serving /us/marriage on policyengine.org since the Vercel rewrite was added (#105) but never had any analytics setup. Page views and conversion events haven't been flowing to GA4, and by extension Google Ads has been reporting zero conversions from the top-converting search keywords (marriage tax calculator, filing jointly vs separately, etc.) since the rewrite went live — confirmed empirically by a 10× drop in reported CvR while the keywords themselves are still performing. Changes: - app/layout.jsx — add the GA4 bootstrap (gtag.js + config) in <head> using the same G-2YHG89FY0N measurement ID as policyengine-app-v2/website/src/app/layout.tsx. Using next/script with afterInteractive so it doesn't block hydration. - app/MarriageApp.jsx — fire the tool_engaged event after 15 seconds on the page, matching the signature and timing used by policyengine-app-v2/website/src/app/[countryId]/[slug]/AppClient.tsx so these conversions aggregate consistently with the other calculator tools. Passes tool_name="marriage" and tool_title="Marriage Tax Calculator". Verification after deploy: - Load /us/marriage in a fresh tab and confirm window.gtag is defined and a /g/collect beacon fires (Chrome DevTools Network tab). - Wait 15 seconds and confirm a second /g/collect beacon fires with en=tool_engaged. - Within ~24-48 hours, Google Ads should start showing conversions against marriage-family keywords again. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PavelMakarchuk
added a commit
to PolicyEngine/policyengine-model
that referenced
this pull request
Apr 22, 2026
This multizone app serves /us/model and /uk/model on policyengine.org via the Vercel rewrite in policyengine-app-v2/website/next.config.ts, but was split out without the GA4 bootstrap that the main website app has — so all page_view events have been silently dropping ever since the multizone rewrite went live. This is the same failure mode we fixed for /us/marriage (PolicyEngine/marriage#114). Applying the same pattern here. Changes: - app/layout.tsx — add the GA4 bootstrap (gtag.js + config) in <head> using the same G-2YHG89FY0N measurement ID as policyengine-app-v2/website/src/app/layout.tsx. Uses next/script with afterInteractive so it doesn't block hydration. - app/client-layout.tsx — documents why this page deliberately does NOT fire the tool_engaged event that the calculator tools fire. tool_engaged is wired up as the Google Ads conversion trigger; firing it on reference content would inflate ad conversion counts. GA4's automatic user_engagement event still tracks engagement time and bounce rate without conflating with the conversion signal. Verification after deploy: - Load https://policyengine.org/us/model in a fresh tab and confirm window.gtag is defined and a /g/collect beacon fires with en=page_view (Chrome DevTools Network tab). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
4 tasks
PavelMakarchuk
added a commit
to PolicyEngine/policyengine-model
that referenced
this pull request
Apr 22, 2026
This multizone app serves /us/model and /uk/model on policyengine.org via the Vercel rewrite in policyengine-app-v2/website/next.config.ts, but was split out without the GA4 bootstrap that the main website app has — so all page_view events have been silently dropping ever since the multizone rewrite went live. This is the same failure mode we fixed for /us/marriage (PolicyEngine/marriage#114). Applying the same pattern here. Changes: - app/layout.tsx — add the GA4 bootstrap (gtag.js + config) in <head> using the same G-2YHG89FY0N measurement ID as policyengine-app-v2/website/src/app/layout.tsx. Uses next/script with afterInteractive so it doesn't block hydration. - app/client-layout.tsx — documents why this page deliberately does NOT fire the tool_engaged event that the calculator tools fire. tool_engaged is wired up as the Google Ads conversion trigger; firing it on reference content would inflate ad conversion counts. GA4's automatic user_engagement event still tracks engagement time and bounce rate without conflating with the conversion signal. Verification after deploy: - Load https://policyengine.org/us/model in a fresh tab and confirm window.gtag is defined and a /g/collect beacon fires with en=page_view (Chrome DevTools Network tab). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 tasks
PavelMakarchuk
added a commit
to PolicyEngine/policyengine-app-v2
that referenced
this pull request
Apr 22, 2026
Guards against a specific cross-repo failure mode we hit in April 2026: when /us/marriage was rewired from the main app to a separate Vercel zone (PolicyEngine/marriage), that zone had no GA4 bootstrap in its layout — so every click silently lost both page_view and the tool_engaged conversion event. Google Ads reported zero conversions from the top-converting keywords for ~5 days before anyone noticed. Neither repo's existing CI could catch it on its own: this PR (routing) and the destination zone (missing layout scripts) lived in different repos. The bug was only detectable by testing the composed system end-to-end. This check runs whenever website/next.config.ts changes. It parses every rewrite destination, fetches the corresponding user-facing policyengine.org URL, and verifies the GA4 measurement ID appears in the served HTML. Fails the PR if any multizone is missing tracking. Skips API-proxy rewrites (Modal backends, PostHog), static-asset proxies (.svg/.png/etc.), and destinations we know don't need tracking (legacy blog, internal slides). Handles :countryId parameter substitution so dynamic routes get tested against a concrete URL (defaults to /us). Example output when healthy: Found 30 multizone destination(s) to audit. ✓ https://policyengine.org/us/marriage gtag present ... 22/22 destinations have tracking. ✅ All multizone destinations have tracking. Example output when broken (the April 2026 incident): ✗ https://policyengine.org/us/marriage GA4 measurement ID missing ❌ 1 destination(s) missing Google Analytics bootstrap: - https://policyengine.org/us/marriage (GA4 measurement ID missing) Fix pattern: add the same gtag setup website/src/app/layout.tsx uses to the destination repo's root layout. See PolicyEngine/marriage#114 and PolicyEngine/policyengine-model#21 for example PRs. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
3 tasks
6 tasks
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.
The bug
This app has been serving
/us/marriageon policyengine.org since the Vercel rewrite was added (#105) but never had any analytics setup. Page views don't fire,tool_engageddoesn't fire, and Google Ads has been reporting zero conversions from every keyword that lands here since the rewrite went live.Evidence
Headless test showed
window.gtagundefined and zero tracking beacons on/us/marriagewhile every other calculator page (e.g./us/aca-reforms-calculator) had working tracking. Reported CvR on the top 3 keywords landing here (marriage tax calculator,filing jointly vs separately,married vs single tax calculator) collapsed from 65–72% to 0% around the time the rewrite was deployed — consistent with silent tracking loss rather than any drop in actual engagement.The fix
Match what
policyengine-app-v2/website/src/app/layout.tsxandAppClient.tsxalready do:app/layout.jsx— add the GA4 bootstrap (gtag.js + config) in<head>using the sameG-2YHG89FY0Nproperty so conversions aggregate with the rest of policyengine.org. Usesnext/scriptwithafterInteractiveso it doesn't block hydration.app/MarriageApp.jsx— firetool_engagedafter 15 seconds on page, matching the threshold and event shape used by the main app'sAppClient.tsx. Passestool_name="marriage"andtool_title="Marriage Tax Calculator".Test plan
After deploy:
https://policyengine.org/us/marriagein a fresh tab. Chrome DevTools → Network. Confirm/gtag/jsloads and/g/collectfires withen=page_view./g/collectfires withen=tool_engaged,ep.tool_name=marriage.PolicyEngine: US→ the marriage-family keywords should start showing conversions again.🤖 Generated with Claude Code