Consentl mit Next.js

Consentl als erstes im <head> des Root-Layouts im App Router, andere Tags über eine kleine ConsentlScript-Komponente, oder das _document des Pages Router.

Loader als erstes im <head>
Ja

Für die vorherige Blockierung und die Standardwerte von Google Consent Mode muss Consentl vor allen anderen Tags laufen, und zwar synchron (kein async, defer oder type="module"). In Next.js bedeutet das ein einfaches <script>-Tag, nicht next/script.

<script src="https://api.consentl.com/v1/loader.js?k=SITE_KEY&l=LANG"></script>

Speichern Sie den Website-Schlüssel in einer Umgebungsvariablen wie NEXT_PUBLIC_CONSENTL_SITE_KEY. Der Schlüssel ist öffentlich, es ist also unbedenklich, ihn dem Browser bereitzustellen. Consent Mode erfordert den Tarif Pro oder höher, die automatische Blockierung bekannter Tracker den Tarif Business.

App Router (app/layout.tsx)

Getestet mit Next.js 16.4 und React 19.

Was React 19 und Next mit Ihren Tags machen

  • React behandelt jedes <script async src> als Ressource und verschiebt es an den Anfang des <head>, über ein einfaches synchrones Skript, auch wenn Sie es nach Consentl geschrieben haben. Auch die eigenen Skripte von Next kommen zuerst.
  • next/script mit strategy="afterInteractive" (und beforeInteractive) schreibt ein <link rel="preload"> für das Tag in die Seite. Der Browser lädt das Tag herunter, während er die Seite liest – vor der Einwilligung. Consentl verhindert seine Ausführung, aber nicht diese Anfrage.

Die gestreamten Seitendaten von Next (self.__next_f) werden von Consentl nie zurückgehalten, egal, was darin vorkommt.

  1. Consentl als einfaches <script src>, als erstes im <head>. React verschiebt es nicht, es steht einmal in der Seite, und Besucher erhalten immer Ihre aktuellen Banner-Einstellungen (der Loader wird 5 Minuten zwischengespeichert).
  2. Jedes andere Tag, das auf eine Einwilligung warten muss (Google Analytics, Meta, TikTok…), über die kleine Komponente ConsentlScript unten. Sie schreibt das Tag als type="text/plain" mit seiner Consentl-Kategorie: React verschiebt es nicht, der Browser lädt es nicht herunter, und Consentl führt es nach der Einwilligung einmal aus, in jedem Tarif, auch nach clientseitiger Navigation.
  3. Oder verwalten Sie diese Tags in Google Tag Manager (siehe Google Tag Manager).

Google-Tags im erweiterten Modus von Consent Mode (ab Tarif Pro) sollen vor der Einwilligung geladen werden; wenn Sie diesen Modus verwenden, laden Sie sie mit next/script und afterInteractive. strategy="lazyOnload" schreibt kein Preload (Next 16.4) und wird bis zur Einwilligung zurückgehalten, aber nur bei Tags, die die automatische Blockierung erkennt (Tarif Business); verwenden Sie daher vorzugsweise ConsentlScript.

// app/consentl-script.tsx — works in Server and Client Components
type Category = "preferences" | "analytics" | "marketing";

/** A tag that waits for consent: inert in the page, run by Consentl once the category is accepted. */
export function ConsentlScript({ category, src, nonce, children }: { category: Category; src?: string; nonce?: string; children?: string }) {
  return src ? (
    // eslint-disable-next-line @next/next/no-sync-scripts -- type="text/plain": never loaded by the browser itself
    <script type="text/plain" data-consentl={category} src={src} nonce={nonce} />
  ) : (
    <script type="text/plain" data-consentl={category} nonce={nonce} dangerouslySetInnerHTML={{ __html: children ?? "" }} />
  );
}
// app/layout.tsx (or app/[locale]/layout.tsx)
import { ConsentlScript } from "./consentl-script";

const SITE_KEY = process.env.NEXT_PUBLIC_CONSENTL_SITE_KEY!;

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const lang = "en"; // e.g. (await params).locale
  const loader = `https://api.consentl.com/v1/loader.js?k=${encodeURIComponent(SITE_KEY)}&l=${encodeURIComponent(lang)}`;
  return (
    <html lang={lang}>
      <head>
        {/* eslint-disable-next-line @next/next/no-sync-scripts -- must block: consent defaults + prior blocking */}
        <script src={loader} />
      </head>
      <body>
        {children}
        <ConsentlScript category="analytics" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX" />
        <ConsentlScript category="analytics">
          {`window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','G-XXXXXXX');`}
        </ConsentlScript>
        <footer>
          <a href="#consentl" data-consentl-open="">Cookie settings</a>
        </footer>
      </body>
    </html>
  );
}

Mit einer Content Security Policy übergeben Sie Ihre Nonce an ConsentlScript (nonce={nonce}); Consentl behält sie bei, wenn es das Tag ausführt.

Warum den Loader nicht inline ausgeben?

Eine frühere Version dieser Seite hat den Loader auf dem Server abgerufen und inline ausgegeben. Im App Router sendet das den Loader bei jedem vollständigen Laden der Seite zweimal, weil React den Inhalt des <script> einer Server Component zusätzlich in die Seitendaten kopiert, und statisch gebaute Seiten behalten die beim Build abgerufene Kopie, bis sie revalidiert werden. Auf derselben Testseite:

Einrichtung HTML der Seite Consentl-Loader
Einfaches Script-Tag (empfohlen) 2,2 KB gzip 9,5 KB gzip, eine Anfrage, 5 Minuten zwischengespeichert und von allen Seiten gemeinsam genutzt
Inline-Loader 15,2 KB gzip, bei jedem vollständigen Laden der Seite zweimal in der Seite

Das einfache Tag kostet eine Anfrage an api.consentl.com, bevor die Seite gerendert wird. Wenn alle anderen Tags in ConsentlScript (oder Google Tag Manager) stehen, gibt es in der Seite nichts, was der Browser herunterladen könnte, bevor Consentl läuft.

next/script mit strategy="beforeInteractive" wird als Preload plus Inline-Eintrag in einer Warteschlange ausgegeben; Next fügt das eigentliche <script> vor der Hydration ein, nach Inline- und asynchronen Skripten, die bereits im serverseitig gerenderten HTML stehen.

Pages Router (pages/_document.tsx)

Im Pages Router wird beforeInteractive mit defer nach den CSS-Links ausgegeben. Verwenden Sie stattdessen ein einfaches Skript:

import { Head, Html, Main, NextScript } from "next/document";

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        {/* eslint-disable-next-line @next/next/no-sync-scripts */}
        <script src={`https://api.consentl.com/v1/loader.js?k=${process.env.NEXT_PUBLIC_CONSENTL_SITE_KEY}&l=en`} />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Clientseitige Navigation

Es ist nichts weiter nötig: Die Laufzeit bleibt geladen, die Auswahl liegt im First-Party-Cookie consentl, und Links „Cookie-Einstellungen“ funktionieren auf jeder Route. Prüfen Sie in Ihrem eigenen Code die Einwilligung mit window.Consentl?.allowed("analytics") und dem Event consentl:change, und starten Sie alles nur einmal (siehe Jede Website).

Prüfen, ob es funktioniert

Verwenden Sie auf einer Seite die Quelltextansicht (nicht den Element-Inspektor): Nach den eigenen Skripten /_next/static/chunks/… von Next muss Consentl vor allen anderen Skripten stehen, es gibt kein <link rel="preload"> für ein Tracking-Tag, und Ihre Tags sind type="text/plain". Öffnen Sie die Website dann in einem privaten Fenster und prüfen Sie das Banner und den Tab „Network“ wie unter Jede Website beschrieben: keine Anfrage an einen Tracker, bevor Sie akzeptieren.

Einschränkungen

  • Im Layout geschriebene async-Tags verschiebt React über Consentl; verwenden Sie stattdessen ConsentlScript.
  • next/script mit afterInteractive lässt den Browser das Tag vor der Einwilligung herunterladen.
  • Mit beforeInteractive laufen Skripte, die bereits im serverseitig gerenderten HTML stehen, vor Consentl.
  • Ein Sprachwechsel im Client lädt das Banner nicht neu; die neue Sprache gilt ab dem nächsten vollständigen Laden der Seite.
  • Wenn Sie eine Content Security Policy senden, erlauben Sie https://api.consentl.com (siehe Jede Website).

Kommen Sie nicht weiter? Schreiben Sie an support@consentl.com – wir antworten an Werktagen auf Englisch.