Next.js & Datenschutz

Cookie-Einwilligung, Google Tag Manager und Google Analytics in Next.js

von Julian Geißler

Quelltext einer Next.js-Anwendung

Einleitung

In der heutigen Webentwicklung ist es entscheidend, den Bedarf an Auswertungen mit dem Schutz der Privatsphäre in Einklang zu bringen. Dieser Leitfaden führt durch die Implementierung einer datenschutzkonformen Google-Analytics-Einrichtung über den Google Tag Manager in einer Next.js-Anwendung – einschließlich einer Einwilligungsverwaltung.

Was bedeutet DSGVO-Konformität?

Die Datenschutz-Grundverordnung verlangt unter anderem:

Was ist der Google Tag Manager?

Der Google Tag Manager ist ein System zur Verwaltung von Tracking-Skripten. Damit können Sie:

Was ist Google Analytics 4?

Google Analytics 4 bietet erweiterte Datenschutzfunktionen, unterstützt den Einwilligungsmodus, erlaubt flexible Formen der Datenerfassung und arbeitet mit dem Google Tag Manager zusammen.

Implementierungsanleitung

Abhängigkeit einrichten

yarn add vanilla-cookieconsent

Tag Manager zunächst mit verweigerter Einwilligung laden

Im Standardzustand werden alle nicht notwendigen Speicherarten verweigert. Ersetzen Sie YOUR_GTM_ID durch die eigene Kennung.

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="de">
      <head>
        <Script id="gtm-script" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('consent', 'default', {
              ad_storage: 'denied',
              analytics_storage: 'denied',
              personalization_storage: 'denied',
              functionality_storage: 'denied',
              ad_user_data: 'denied',
              ad_personalization: 'denied'
            });

            (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','YOUR_GTM_ID');
          `}
        </Script>
      </head>
      <body>{children}</body>
    </html>
  );
}

Banner für die Cookie-Einwilligung implementieren

Die Einwilligungsverwaltung sollte auf die Datenschutzerklärung verweisen, eine Auswahl zwischen allen und nur notwendigen Diensten ermöglichen und den Einwilligungszustand des Tag Managers aktualisieren.

// components/CookieConsent.tsx
'use client';

import { createContext, useContext, useEffect, useState } from 'react';
import 'vanilla-cookieconsent/dist/cookieconsent.css';
import * as CookieConsent from 'vanilla-cookieconsent';

const CookieConsentContext = createContext<string[]>([]);
export const useCookieConsent = () => useContext(CookieConsentContext);

export function CookieConsentProvider({ children }) {
  const [acceptedServices, setAcceptedServices] = useState<string[]>([]);

  useEffect(() => {
    const updateGtmConsent = () => {
      if (typeof window.gtag !== 'function') return;

      const preferences = CookieConsent.getUserPreferences();
      const categories = preferences.acceptedCategories || [];
      const services = preferences.acceptedServices || {};
      const googleAccepted =
        categories.includes('analytics') &&
        services.analytics?.includes('google');

      window.gtag('consent', 'update', {
        analytics_storage: googleAccepted ? 'granted' : 'denied',
        ad_storage: googleAccepted ? 'granted' : 'denied',
        personalization_storage: googleAccepted ? 'granted' : 'denied',
        functionality_storage: 'granted',
        ad_user_data: googleAccepted ? 'granted' : 'denied',
        ad_personalization: googleAccepted ? 'granted' : 'denied'
      });
    };

    const updateAcceptedServices = () => {
      const services = CookieConsent.getUserPreferences().acceptedServices || {};
      setAcceptedServices(Object.values(services).flat());
    };

    const updateConsent = () => {
      updateAcceptedServices();
      updateGtmConsent();
    };

    CookieConsent.run({
      cookie: {
        domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN || 'ihre-domain.de'
      },
      categories: {
        necessary: { enabled: true, readOnly: true },
        analytics: {
          services: { google: { label: 'Google Analytics' } }
        }
      },
      language: {
        default: 'de',
        translations: {
          de: {
            consentModal: {
              title: 'Datenschutzeinstellungen',
              description: 'Wir verwenden Cookies. Bitte wählen Sie Ihre Einstellungen.',
              acceptAllBtn: 'Alle akzeptieren',
              acceptNecessaryBtn: 'Nur notwendige akzeptieren',
              showPreferencesBtn: 'Einstellungen verwalten'
            },
            preferencesModal: {
              title: 'Cookie-Einstellungen',
              sections: [
                {
                  title: 'Notwendig',
                  description: 'Für die Funktion der Website erforderlich.',
                  linkedCategory: 'necessary'
                },
                {
                  title: 'Analyse',
                  description: 'Hilft uns, die Nutzung der Website zu verstehen.',
                  linkedCategory: 'analytics'
                }
              ],
              acceptAllBtn: 'Alle akzeptieren',
              acceptNecessaryBtn: 'Nur notwendige akzeptieren',
              savePreferencesBtn: 'Auswahl speichern'
            }
          }
        }
      },
      onChange: updateConsent,
      onFirstConsent: updateConsent
    });

    if (CookieConsent.getUserPreferences()?.acceptedCategories) {
      updateConsent();
    }
  }, []);

  return (
    <CookieConsentContext.Provider value={acceptedServices}>
      {children}
    </CookieConsentContext.Provider>
  );
}

Legen Sie die eigene Domain fest oder verwenden Sie dafür wie im Beispiel eine Umgebungsvariable.

Den Anbieter einbinden

// app/providers.tsx
'use client';

import { CookieConsentProvider } from '@/components/CookieConsent';

export default function Providers({ children }) {
  return <CookieConsentProvider>{children}</CookieConsentProvider>;
}

Der Anbieter wird anschließend im Layout um den eigentlichen Seiteninhalt gelegt:

// app/layout.tsx
import Providers from '@/app/providers';

export default function RootLayout({ children }) {
  return (
    <html lang="de">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Weitere Tracking-Dienste ergänzen

Datenschutzseite zur Next.js-Anwendung hinzufügen

Nutzer sollten ihre Auswahl später wieder öffnen und ändern können. Dafür reicht eine Schaltfläche auf der Datenschutzseite:

// app/datenschutz/page.tsx
'use client';

import * as CookieConsent from 'vanilla-cookieconsent';

export default function Datenschutz() {
  return (
    <main>
      <h1>Datenschutzeinstellungen</h1>
      <p>Hier können Sie Ihre Cookie-Auswahl jederzeit ändern.</p>
      <button onClick={() => CookieConsent.showPreferences()}>
        Cookie-Einstellungen öffnen
      </button>
    </main>
  );
}

Zusammenfassung

Die Umsetzung berücksichtigt die Datenschutzrechte der Nutzer, die technische Einbindung, die Verwaltung von Einwilligungen und einen nachvollziehbaren Umgang mit Daten.

Wenn Sie diesen Leitfaden befolgen, entsteht eine solide Analyseeinrichtung, die die Privatsphäre der Nutzer respektiert und gleichzeitig Einblicke in die Nutzung der Anwendung ermöglicht.

Weiterführende Links

Gruß
Julian