ga4-analytics-nextjs
Skill0 starsChanged 3 months agoMITView on GitHub
Integrates Google Analytics 4 in Next.js App Router: @next/third-parties, env-based enablement, typed event tracking, and SPA page views. Use when adding GA4, Google Analytics, conversion tracking, or product analytics to a Next.js webapp.
- Reads credentials
- Installs packages
In this repository23 other files it gives its agents.
- client-document-exportSkill
- cloudflare-turnstile-nextjsSkill
- git-repo-privacy-auditSkill
- hardened-contact-form-nextjsSkill
- html-semantic-markupSkill
- multi-step-wizard-flowSkill
- nextjs-api-routesSkill
- nextjs-app-routerSkill
- nextjs-seo-metadataSkill
- nextjs-ui-foundationSkill
- next-themes-dark-modeSkill
- playwright-e2e-testingSkill
- react-components-patternsSkill
- react-hooks-patternsSkill
- recharts-dashboardSkill
- resend-email-nextjsSkill
- server-pdf-playwrightSkill
- shadcn-ui-setupSkill
- specialized-form-inputsSkill
- tailwind-css-v4Skill
- vitest-unit-testingSkill
- web-accessibility-wcagSkill
- zustand-persist-stateSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 117
Use snake_case event names.
- Distinctive: line 134
Document analytics in privacy policy
- Distinctive: line 135
Consider cookie consent banner for EU (load GA after consent if required)
- Distinctive: line 136
Avoid PII in event params (no raw emails/names)
- Distinctive: line 137
Use GA4 data retention settings in admin console
- Very specific: line 142
Verify window.gtag exists after load
- Distinctive: line 143
Check ad blockers if events missing locally
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
GA4 Analytics in Next.js
Install
npm install @next/third-partiesEnvironment
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXXOptional — app works without it. Never commit real IDs to public repos if policy requires secrets (usually GA ID is public).
export function getPublicGaId(): string | undefined {
return process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
}Root layout wiring
import { GoogleAnalytics } from "@next/third-parties/google";
import { getPublicGaId } from "@/lib/analytics-config";
export default function RootLayout({ children }) {
const gaId = getPublicGaId();
return (
<html>
<body>
{children}
{gaId ? <GoogleAnalytics gaId={gaId} /> : null}
</body>
</html>
);
}Loads gtag only when ID present — safe for local dev.
Typed event helper (lib/analytics.ts)
export type AnalyticsEvent =
| "page_view"
| "cta_click"
| "form_submit"
| "form_submit_success"
| "form_submit_error";
declare global {
interface Window {
gtag?: (...args: unknown[]) => void;
}
}
export function trackEvent(
event: AnalyticsEvent,
params?: Record<string, string | number | boolean | undefined>
) {
if (typeof window === "undefined" || !window.gtag) return;
const cleaned = params
? Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined))
: undefined;
window.gtag("event", event, cleaned);
}Extend AnalyticsEvent union as features ship — keeps naming consistent.
SPA page views (App Router)
GoogleAnalytics handles initial load. For client navigations, add a provider:
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { trackPageView } from "@/lib/analytics";
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
const url = pathname + (searchParams?.toString() ? `?${searchParams}` : "");
trackPageView(url);
}, [pathname, searchParams]);
return <>{children}</>;
}Wrap in layout inside <body>.
What to track
| Category | Events |
| Marketing | cta_get_started, nav_click |
| Funnel | wizard_step_view, wizard_complete |
| Features | export_download, email_send_success |
| Errors | form_submit_error with error_message param |
Use snake_case event names. Pass structured params (step, source, variant) not concatenated strings.
Client usage
<Button onClick={() => {
trackEvent("cta_click", { location: "hero", label: "get_started" });
router.push("/signup");
}}>
Get Started
</Button>Pick one tracking mechanism per element — either trackEvent() in onClick or data-analytics-* attributes with a delegated listener, not both (duplicate GA events).
Privacy / compliance
- Document analytics in privacy policy
- Consider cookie consent banner for EU (load GA after consent if required)
- Avoid PII in event params (no raw emails/names)
- Use GA4 data retention settings in admin console
Debugging
- GA4 DebugView (enable debug mode via Chrome extension or
debug_mode: trueparam) - Verify
window.gtagexists after load - Check ad blockers if events missing locally
Anti-patterns
- Importing gtag scripts manually when
@next/third-partiesexists - Untyped string events scattered across codebase
- Tracking before consent when legally required
- Duplicate events from both
trackEvent()anddata-analytics-eventon the same click
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

