Oliver Wolfson
ServicesProjectsContact

Development Services

SaaS apps · AI systems · MVP builds · Technical consulting

Services·Blog
© 2026 O. Wolf. All rights reserved.
Web DevelopmentNext.jsPrivacy
Setting Up Google Analytics with GDPR Compliance in Next.js 15
A complete guide to implementing Google Analytics with proper GDPR compliance in Next.js 15, including cookie consent management and consent mode
August 23, 2025•O. Wolfson

This guide demonstrates how we implemented Google Analytics with full GDPR compliance in a Next.js 15 application using the app router. Our solution features granular cookie consent management, Google Analytics Consent Mode v2, and a privacy-first approach that ensures compliance with European data protection regulations.

Our Application Setup

Our Next.js 15 application uses the app router with the following key characteristics:

  • Framework: Next.js 15 with app router
  • Styling: Tailwind CSS with custom design system
  • Fonts: Geist Sans and Geist Mono from Google Fonts
  • Theme System: Custom dark/light mode theme provider
  • Layout: Responsive design with mobile-first approach
  • Privacy Focus: Built-in GDPR compliance from the ground up

The application structure includes a main layout with header, navigation, and footer components, all wrapped in a theme provider that supports system preferences and manual theme switching.

Why We Built This Solution

European GDPR regulations require websites to obtain explicit user consent before setting analytics cookies. Our solution addresses several key requirements:

Legal Compliance: We implement Google Analytics Consent Mode v2, which allows analytics to function in a privacy-preserving way even before user consent, then upgrades to full tracking once consent is granted.

User Control: Users can granularly control different cookie categories (necessary, analytics, preferences, marketing) and easily modify their preferences at any time.

Developer Experience: The system integrates seamlessly with Next.js 15's app router without causing build issues or requiring complex workarounds.

Performance: Analytics scripts only load in production, and consent preferences are stored locally for immediate application on subsequent visits.

Core Architecture

Our implementation consists of four main components:

  1. Root Layout Integration: Google Analytics scripts with consent mode configuration
  2. Consent Manager: React component that handles consent state and localStorage persistence
  3. UI Components: Cookie consent banner and preferences manager
  4. Analytics Library: Utility functions for consent management and event tracking

The system defaults to denying all non-essential cookies, displays a consent banner to new visitors, and applies user preferences immediately upon consent.

Implementation Walkthrough

Environment Configuration

We store the Google Analytics ID in environment variables for security and flexibility across environments:

# .env.local
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

This approach allows us to use different analytics properties for development, staging, and production environments while keeping sensitive configuration out of version control.

Root Layout Integration

Our root layout component (src/app/layout.tsx) integrates Google Analytics with consent mode:

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const GA_ID = process.env.NEXT_PUBLIC_GA_ID;

  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        {/* Google Analytics - Only load in production and after consent */}
        {process.env.NODE_ENV === "production" && GA_ID && (
          <>
            <Script
              strategy="afterInteractive"
              src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
            />
            <Script id="google-analytics" strategy="afterInteractive">
              {`
                window.dataLayer = window.dataLayer || [];
                function gtag(){dataLayer.push(arguments);}
                gtag('js', new Date());
                
                // Set default consent to denied - will be updated when user consents
                gtag('consent', 'default', {
                  'analytics_storage': 'denied',
                  'ad_storage': 'denied',
                  'functionality_storage': 'denied',
                  'personalization_storage': 'denied',
                  'security_storage': 'granted'
                });
                
                // Initialize GA with consent mode
                gtag('config', '${GA_ID}', {
                  'consent_mode': 'advanced'
                });
              `}
            </Script>
          </>
        )}
      </head>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ThemeProvider
          attribute="class"
          defaultTheme="dark"
          enableSystem
          disableTransitionOnChange
        >
          <ThemeBridge />
          <div className="flex flex-col min-h-screen px-4 sm:px-36">
            <LinksSheet />
            <Header />
            <main className="flex-1 flex flex-col min-h-0 overflow-y-auto overflow-x-hidden sm:pt-40 pt-32">
              {children}
              <CookieConsentManager />
            </main>
            <Footer />
          </div>
        </ThemeProvider>
      </body>
    </html>
  );
}

Key Implementation Details:

  • Production-Only Loading: Analytics scripts only load when NODE_ENV === "production", preventing development tracking
  • Consent Mode v2: We initialize Google Analytics with all consent categories denied by default, except security_storage which remains granted
  • Advanced Consent Mode: This allows Google Analytics to collect anonymous, aggregated data even before explicit consent
  • Component Placement: The CookieConsentManager is placed within the main content area for proper styling and accessibility

Cookie Consent Management System

Our consent manager (src/components/compliance/cookie-consent-manager.tsx) handles the complete consent lifecycle:

export interface CookieConsent {
  necessary: boolean; // Always true, can't be disabled
  analytics: boolean; // Google Analytics
  preferences: boolean; // User preferences
  marketing: boolean; // Future marketing cookies
  timestamp: number;
  version: string;
}

const DEFAULT_CONSENT: CookieConsent = {
  necessary: true,
  analytics: false,
  preferences: false,
  marketing: false,
  timestamp: Date.now(),
  version: "2.0",
};

The consent manager implements several key patterns:

State Management: Uses React hooks to manage consent state and banner visibility, with immediate localStorage persistence.

Consent Application: When consent changes, the system immediately applies new settings to Google Analytics using the consent update API.

Error Handling: Gracefully handles localStorage parsing errors and provides fallback behavior.

Version Control: Includes consent version tracking for future consent requirement changes.

Analytics Library

Our analytics utility library (src/lib/analytics.ts) provides clean interfaces for consent management:

// Consent management for GDPR compliance
export const updateConsent = (consentSettings: {
  analytics_storage?: "granted" | "denied";
  ad_storage?: "granted" | "denied";
  functionality_storage?: "granted" | "denied";
  personalization_storage?: "granted" | "denied";
  security_storage?: "granted" | "denied";
}) => {
  if (typeof window !== "undefined" && window.gtag) {
    window.gtag("consent", "update", consentSettings);
  }
};

This abstraction layer provides type-safe consent management and ensures the gtag API is available before calling it.

User Interface Components

Our consent banner provides a developer-themed interface matching our application's aesthetic:

Progressive Disclosure: The banner initially shows simple accept/reject options, with a "customize" button that reveals granular controls.

Accessibility: Full keyboard navigation, ARIA labels, and semantic HTML structure.

Visual Design: Matches our application's theme system with consistent typography and color schemes.

Responsive Design: Works seamlessly across mobile and desktop viewports.

The banner uses function-style button labels (acceptAll(), rejectAll(), customize()) that align with our application's developer-focused branding.

Testing Our Implementation

We test the implementation across several scenarios:

Initial Visit: New users see the consent banner with analytics disabled by default.

Consent Persistence: User preferences persist across browser sessions and page reloads.

Analytics Activation: Google Analytics only begins full tracking after explicit user consent.

Preference Changes: Users can modify their consent choices, with changes applied immediately.

Production Verification: Analytics scripts only load in production builds, preventing development environment tracking.

Production Deployment

Our deployment process ensures proper analytics functionality:

  1. Environment Variables: Set NEXT_PUBLIC_GA_ID in production environment
  2. Build Verification: Confirm analytics scripts only appear in production builds
  3. Consent Flow Testing: Verify banner appears for new users and preferences persist
  4. Analytics Validation: Check Google Analytics receives data only after user consent

Key Benefits of Our Approach

Privacy-First: Default denial of all tracking with explicit opt-in requirement meets GDPR standards.

Performance Optimized: Analytics scripts only load when needed, reducing bundle size in development.

User Experience: Clean, intuitive interface that doesn't overwhelm users with complex privacy options.

Maintainable Code: Clear separation of concerns with reusable components and type-safe interfaces.

Future-Proof: Structured to easily accommodate additional cookie categories or consent requirements.

This implementation provides a solid foundation for GDPR-compliant analytics in Next.js 15 applications while maintaining excellent developer and user experiences.


Tech Stack: Next.js 15, React, TypeScript, Tailwind CSS, Google Analytics 4 Compliance: GDPR, Google Consent Mode v2 Key Features: Granular consent, localStorage persistence, production-only loading

Tags
#google-analytics#gdpr#nextjs#privacy#cookies#compliance