agentleFS
bkjain655bkjain655/agent-skills/skills/ga4-analytics-nextjs/SKILL.md

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

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
  1. Very specific:

    Use snake_case event names.

    line 117
  2. Distinctive:

    Document analytics in privacy policy

    line 134
  3. Distinctive:

    Consider cookie consent banner for EU (load GA after consent if required)

    line 135
  4. Distinctive:

    Avoid PII in event params (no raw emails/names)

    line 136
  5. Distinctive:

    Use GA4 data retention settings in admin console

    line 137
  6. Very specific:

    Verify window.gtag exists after load

    line 142
  7. Distinctive:

    Check ad blockers if events missing locally

    line 143

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

GA4 Analytics in Next.js

Install

npm install @next/third-parties

Environment

NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

Optional — 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

CategoryEvents
Marketingcta_get_started, nav_click
Funnelwizard_step_view, wizard_complete
Featuresexport_download, email_send_success
Errorsform_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: true param)
  • Verify window.gtag exists after load
  • Check ad blockers if events missing locally

Anti-patterns

  • Importing gtag scripts manually when @next/third-parties exists
  • Untyped string events scattered across codebase
  • Tracking before consent when legally required
  • Duplicate events from both trackEvent() and data-analytics-event on the same click

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.