← Back to Blog·March 5, 2026·9 min read·Platform

How to Add Analytics to Your Next.js App

Next.js isn't a typical SPA — it uses server-side rendering, React Server Components, and the App Router. Here's how to set up analytics that actually work with all of it.

Next.js analytics setup guide showing code editor with analytics integration

At a Glance

  • Next.js uses SSR, RSC, and client-side navigation — analytics must handle all three rendering modes.
  • Copper Analytics (recommended) — one script tag, automatic SPA route tracking, under 2 KB, free tier.
  • Google Analytics works via next/script but requires cookie consent banners and adds ~90 KB.
  • Plausible is privacy-first and lightweight (<1 KB) but starts at $9/month with no free tier.
  • Vercel Analytics is built-in but limited to Vercel-hosted apps and basic metrics only.

Why Next.js Apps Need Different Analytics Setup

If you've ever dropped a Google Analytics snippet into a plain HTML site, you know the process: paste a script tag in the <head>, done. But Next.js is not a plain HTML site. It introduces three rendering patterns that fundamentally change how analytics scripts load and fire.

Server-Side Rendering (SSR) means your pages are rendered on the server before reaching the browser. Analytics scripts that depend onwindow or document will throw errors if they execute during SSR. You need to ensure your tracking code only runs client-side.

React Server Components (RSC), introduced in the App Router, render entirely on the server and never ship JavaScript to the client. You cannot place analytics code inside a Server Component — it must live in a Client Component or be loaded via the next/script component.

Client-Side Navigation is the third challenge. After the initial page load, Next.js uses client-side routing for subsequent navigations. A traditional analytics script only fires on full page loads, so it misses every route change after the first one. You need an analytics tool that either automatically detects SPA route changes or provides hooks to track them manually.

The App Router (introduced in Next.js 13) and the Pages Router handle navigation differently, which affects how you wire up route tracking. We'll cover both patterns below.

Key Takeaway

Any analytics tool you choose for Next.js must handle three things: client-only execution, SPA route change detection, and compatibility with the next/script component. Tools that handle all three automatically save you the most setup time.

Option 1: Copper Analytics (Recommended)

Copper Analytics is a privacy-first analytics platform with automatic SPA route tracking, no cookies, and a script size under 2 KB. It works with both the App Router and Pages Router out of the box — no additional configuration needed for route changes.

The setup takes under five minutes. Create a CopperAnalytics component and include it in your root layout:

src/components/CopperAnalytics.tsx
import Script from 'next/script';

export function CopperAnalytics() {
  return (
    <Script
      src="https://analytics.copper.io/tracker.js"
      data-site-id="YOUR_SITE_ID"
      strategy="afterInteractive"
    />
  );
}

Then add it to your root layout:

src/app/layout.tsx
import { CopperAnalytics } from '@/components/CopperAnalytics';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <CopperAnalytics />
      </body>
    </html>
  );
}

That's it. The Copper script automatically detects client-side route changes using the History API, so you don't need to add any event listeners or use Next.js router hooks. It works identically in the App Router and Pages Router.

  • Automatic SPA tracking: No manual route change handling required for either router.
  • Privacy-first: No cookies, no personal data, GDPR-compliant without consent banners.
  • Tiny footprint: Under 2 KB — negligible impact on Core Web Vitals.
  • Free tier: Start tracking immediately without a credit card.
  • AI crawler tracking: See which AI bots (GPTBot, ClaudeBot, etc.) crawl your pages.
  • Real-time dashboard: Visitor data appears instantly, not in batched intervals.

For a complete walkthrough, see our guide on setting up website analytics in 5 minutes.

Option 2: Google Analytics (GA4) with next/script

Google Analytics 4 is the most widely used analytics platform in the world. If your team is already invested in the Google ecosystem — Google Ads, Search Console, Looker Studio — GA4 provides deep integration across all of them.

To add GA4 to a Next.js app, use the next/script component to load the gtag.js library. Create a reusable component:

src/components/GoogleAnalytics.tsx
'use client';

import Script from 'next/script';

const GA_ID = 'G-XXXXXXXXXX';

export function GoogleAnalytics() {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
        strategy="afterInteractive"
      />
      <Script id="ga4-init" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${GA_ID}', {
            page_path: window.location.pathname,
          });
        `}
      </Script>
    </>
  );
}

Add this component to your root layout the same way as the Copper example above. GA4's gtag.js script does detect some client-side navigation automatically via its “enhanced measurement” feature, but it can miss edge cases in Next.js. For reliable tracking, you may want to add manual route change events (covered in the Route Change Tracking section below).

Tradeoffs

  • Script size: gtag.js loads ~90 KB of JavaScript, which impacts LCP and page load speed.
  • Cookie consent: GA4 sets cookies and collects personal data, so you must show a consent banner in the EU.
  • Complexity: GA4's event model has a steep learning curve compared to simpler tools.
  • Data ownership: Your analytics data lives on Google's servers under their terms of service.
  • Free: GA4 is free for most sites, which makes it the default choice for budget-constrained teams.

Performance Note

The ~90 KB gtag.js bundle is one of the largest analytics scripts available. On mobile connections, this can measurably increase your Largest Contentful Paint (LCP) and Total Blocking Time. Use strategy="lazyOnload" if you want to defer loading until after the page is interactive, though this means you'll miss some early pageview data.

Option 3: Plausible Analytics

Plausible is an open-source, privacy-first analytics tool with a tracking script under 1 KB. Like Copper, it requires no cookies and is GDPR-compliant out of the box. The setup for Next.js is nearly identical to the other script-based options:

src/components/PlausibleAnalytics.tsx
import Script from 'next/script';

export function PlausibleAnalytics() {
  return (
    <Script
      src="https://plausible.io/js/script.js"
      data-domain="yourdomain.com"
      strategy="afterInteractive"
    />
  );
}

Plausible's script automatically tracks SPA route changes via the History API, similar to Copper. No extra configuration is needed for the App Router or Pages Router.

Tradeoffs

  • Smallest script: Under 1 KB — the lightest analytics script available.
  • Open source: Self-host if you want complete control over your data.
  • No free tier: Starts at $9/month for 10K pageviews. There is no permanent free plan.
  • Limited features: No AI crawler tracking, no Web Vitals monitoring, no real-time view.
  • EU-hosted: All managed data stays in the European Union.

Option 4: Vercel Analytics (Built-In but Limited)

If you deploy on Vercel, you get access to Vercel Analytics and Vercel Speed Insights — two built-in tools that integrate directly with the platform. Setup is minimal since you're already in the Vercel ecosystem:

Terminal
pnpm add @vercel/analytics @vercel/speed-insights
src/app/layout.tsx
import { Analytics } from '@vercel/analytics/next';
import { SpeedInsights } from '@vercel/speed-insights/next';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <Analytics />
        <SpeedInsights />
      </body>
    </html>
  );
}

Route changes are tracked automatically. The integration is clean and requires zero configuration. However, there are significant limitations:

Tradeoffs

  • Vercel-only: Only works on Vercel-hosted deployments. If you move to AWS, Cloudflare, or a VPS, you lose your analytics.
  • Basic metrics: Tracks pageviews, top pages, referrers, countries, and devices. No custom events, no conversion tracking, no funnel analysis.
  • Free tier limits: The Hobby plan includes 2,500 events/month. Pro plans include 25K events/month, with overages billed at $14 per additional 25K.
  • No data export: Limited ability to extract your raw data for custom reporting.
  • Vendor lock-in: Your analytics are tied to your hosting provider, which limits flexibility.

When Vercel Analytics Makes Sense

Vercel Analytics is a reasonable choice if you're already on Vercel, need only basic pageview data, and don't want to set up a separate tool. For anything beyond basic metrics — custom events, privacy compliance, AI crawler visibility — you'll need a dedicated analytics platform.

Route Change Tracking: App Router vs Pages Router

Most modern analytics tools (Copper, Plausible, GA4 with enhanced measurement) automatically detect SPA route changes via the History API. If your tool handles this automatically, you don't need the code below. But if you need manual tracking — for example, with a custom analytics endpoint or an older GA4 setup — here's how to do it in each router.

App Router (Next.js 13+)

In the App Router, use the usePathname and useSearchParams hooks from next/navigation inside a Client Component:

src/components/RouteTracker.tsx (App Router)
'use client';

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

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

  useEffect(() => {
    const url = pathname + (searchParams?.toString()
      ? '?' + searchParams.toString()
      : '');

    // Replace with your analytics call
    console.log('Page view:', url);
    // Example: window.gtag?.('event', 'page_view', { page_path: url });
  }, [pathname, searchParams]);

  return null;
}

Place this component in your root layout alongside your analytics script. It will fire on every route change, including query string updates.

Pages Router

In the Pages Router, use the router.events API inside _app.tsx:

src/pages/_app.tsx (Pages Router)
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();

  useEffect(() => {
    const handleRouteChange = (url: string) => {
      // Replace with your analytics call
      console.log('Page view:', url);
      // Example: window.gtag?.('event', 'page_view', { page_path: url });
    };

    router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);

  return <Component {...pageProps} />;
}

The routeChangeComplete event fires after every client-side navigation. Note that router.events does not exist in the App Router — it's a Pages Router-only API.

Pro Tip

If you use Copper Analytics or Plausible, you can skip the manual route tracking code entirely. Both tools listen for pushState and replaceState calls automatically, which covers both the App Router and Pages Router without any extra setup.

Performance Comparison: Script Size and Loading Strategy

Performance matters in Next.js apps, especially if you care about Core Web Vitals and SEO. The size of your analytics script directly impacts Largest Contentful Paint (LCP) and Total Blocking Time (TBT). Here's how the four options compare:

ToolScript SizeCookiesAuto SPA TrackingFree Tier
Copper Analytics<2 KBNoneYesYes
Google Analytics (GA4)~90 KBYes (requires consent)PartialYes
Plausible<1 KBNoneYesNo ($9/mo min)
Vercel Analytics~1 KBNoneYesLimited (2.5K events)

The next/script component provides three loading strategies that control when the script executes:

  • beforeInteractive: Loads before hydration. Use only for critical scripts — never for analytics. This blocks rendering.
  • afterInteractive: Loads after hydration but before idle. This is the default and recommended strategy for analytics.
  • lazyOnload: Loads during browser idle time. Best for non-critical scripts. Minimizes performance impact but may miss early pageview data.

For most analytics tools, afterInteractive is the right choice. It ensures the script loads quickly enough to capture the initial pageview without blocking the critical rendering path. If you use GA4 and want to minimize its performance impact, lazyOnload is worth testing — but measure the data loss before committing to it in production.

Our Recommendation: Use Copper Analytics

For Next.js developers who want analytics that just work without compromising on performance or privacy, Copper Analytics is the best option. Here's why:

  • Zero-config SPA tracking: Works with both App Router and Pages Router automatically. No usePathname hooks, no router.events listeners, no manual pageview calls.
  • Performance-first: At under 2 KB, the Copper script is 45x smaller than GA4's gtag.js. Your Lighthouse score stays intact.
  • No consent banners: Cookie-free, GDPR-compliant analytics means no consent management platform, no popup fatigue, no legal complexity.
  • AI crawler visibility: See which AI bots are crawling your Next.js app — critical as AI-powered search reshapes web traffic patterns.
  • Real-time data: Visitor data appears in your real-time dashboard instantly, not in 24-48 hour batches like GA4.
  • Free to start: No credit card required. Get analytics running on your Next.js app in under five minutes.

If you're already deep in the Google ecosystem and need GA4's integration with Ads and Looker Studio, Google Analytics is the practical choice despite the performance cost. If you want open-source purity and EU data hosting, Plausible is excellent. And if you're on Vercel and only need basic pageview counts, Vercel Analytics will cover you with no setup.

But for the majority of Next.js developers who want lightweight, privacy-first, feature-rich analytics with a free tier — Copper Analytics is the right tool.

Ready to Get Started?

Sign up for a free Copper Analytics account, grab your site ID, and paste the two code snippets from Option 1 into your Next.js app. You'll have analytics running in under five minutes. See our 5-minute setup guide for the full walkthrough.

Try Copper Analytics Free

Privacy-first analytics built for Next.js. Automatic SPA tracking, AI crawler visibility, and real-time data. No cookies. No consent banners. Free tier available.

Get Started Free