00.00.00

Blog

Blogs/google-analytics-comparison

Next.jsAnalyticsSEO

GA4 vs GTM vs G Tag: Understanding Google Analytics Tools for Next.js

A comprehensive guide to GA4, GTM, and gtag.js — what they are, how they work together, and how to implement them in Next.js.

May 4, 202510 min—views

When diving into analytics for your Next.js application, you’ll encounter three main Google tools: GA4, GTM, and G tag. Let’s break down what they are, how they work together, and exactly how to implement them.

Understanding the Google Analytics ecosystem

  • GA4 — the analytics platform that stores, processes, and visualizes your data
  • gtag.js — the JavaScript library that sends data from your site to Google
  • GTM — a tag manager so you can control multiple tracking tags without redeploying

Think of them as: GA4 = dashboard, gtag.js = data pipeline, GTM = control center.

Option 1: Direct GA4 with gtag.js

Best for simple websites with basic tracking needs.

jsxapp/layout.jsx
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <Script
          src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_ID}`}
          strategy="afterInteractive"
        />
        <Script id="google-analytics" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${process.env.NEXT_PUBLIC_GA_ID}');
          `}
        </Script>
      </head>
      <body>{children}</body>
    </html>
  );
}

Option 2: Advanced setup with GTM

Best for complex tracking, multiple marketing tags, or frequent changes.

jsxapp/layout.jsx
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <Script id="gtm-script" strategy="afterInteractive">
          {`
            (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
            j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
            'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
            })(window,document,'script','dataLayer','${process.env.NEXT_PUBLIC_GTM_ID}');
          `}
        </Script>
      </head>
      <body>
        <noscript>
          <iframe
            src={`https://www.googletagmanager.com/ns.html?id=${process.env.NEXT_PUBLIC_GTM_ID}`}
            height="0"
            width="0"
            style={{ display: "none", visibility: "hidden" }}
          />
        </noscript>
        {children}
      </body>
    </html>
  );
}

Tracking page views in Next.js

Since Next.js uses client-side routing, manually track page views after the initial load:

jsxcomponents/analytics-tracker.jsx
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";

export function AnalyticsTracker() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (typeof window.gtag === "function") {
      window.gtag("event", "page_view", {
        page_path: pathname + searchParams.toString(),
      });
    }
  }, [pathname, searchParams]);

  return null;
}

Data layer events with GTM

jsxcomponents/buy-button.jsx
"use client";

export function BuyButton({ productId, price, name }) {
  const handlePurchase = () => {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: "purchase",
      ecommerce: {
        transaction_id: generateOrderId(),
        value: price,
        currency: "USD",
        items: [{ item_id: productId, item_name: name, price }],
      },
    });
  };

  return <button onClick={handlePurchase}>Buy Now</button>;
}

Consent-aware loading

Only load analytics after the user consents:

jsxcomponents/analytics.jsx
"use client";

import { useState, useEffect } from "react";
import Script from "next/script";

export function Analytics() {
  const [consent, setConsent] = useState(false);

  useEffect(() => {
    setConsent(localStorage.getItem("analyticsConsent") === "true");
  }, []);

  if (!consent) return null;

  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_ID}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${process.env.NEXT_PUBLIC_GA_ID}');
        `}
      </Script>
    </>
  );
}

When to use which

  • Direct GA4 — simple pageviews/events, you own the code, rare changes
  • GTM — multiple marketing tags, non-devs update tracking, complex campaigns
Start simple with gtag.js. Move to GTM when your tracking needs grow — and always respect consent.