Consentl met Next.js

Plaats Consentl als eerste in de <head> van de root layout (App Router), laad andere tags via een kleine ConsentlScript-component, of gebruik _document (Pages Router).

Loader als eerste in <head>
Ja

Voor voorafgaande blokkering en de standaardwaarden van Google Consent Mode moet Consentl vóór elke andere tag worden uitgevoerd, synchroon (geen async, defer of type="module"). In Next.js betekent dat een gewone <script>-tag, niet next/script.

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

Sla de sitesleutel op in een omgevingsvariabele zoals NEXT_PUBLIC_CONSENTL_SITE_KEY. De sleutel is openbaar, dus het is geen probleem dat de browser hem ziet. Consent Mode vereist het plan Pro of hoger, automatische blokkering van bekende trackers het plan Business.

App Router (app/layout.tsx)

Getest met Next.js 16.4 en React 19.

Wat React 19 en Next met uw tags doen

  • React behandelt elke <script async src> als een resource en verplaatst die naar de bovenkant van <head>, boven een gewoon synchroon script, zelfs als u hem na Consentl hebt geschreven. De eigen scripts van Next komen ook eerst.
  • next/script met strategy="afterInteractive" (en beforeInteractive) schrijft een <link rel="preload"> voor de tag in de pagina. De browser downloadt de tag terwijl hij de pagina leest, vóór de toestemming. Consentl voorkomt dat de tag wordt uitgevoerd, maar niet dat verzoek.

De gestreamde paginagegevens van Next (self.__next_f) worden nooit door Consentl tegengehouden, wat er ook in staat.

  1. Consentl als gewone <script src>, als eerste in <head>. React verplaatst hem niet, hij staat één keer in de pagina, en bezoekers krijgen altijd uw actuele bannerinstellingen (de loader wordt 5 minuten in de cache opgeslagen).
  2. Elke andere tag die op toestemming moet wachten (Google Analytics, Meta, TikTok…) via de kleine component ConsentlScript hieronder. Die schrijft de tag als type="text/plain" met zijn Consentl-categorie: React verplaatst hem niet, de browser downloadt hem niet en Consentl voert hem na de toestemming één keer uit, bij elk plan, ook na navigatie aan de clientkant.
  3. Of beheer die tags in Google Tag Manager (zie Google Tag Manager).

Google-tags in de geavanceerde modus van Consent Mode (plan Pro en hoger) zijn bedoeld om vóór de toestemming te laden; als u die modus gebruikt, laad ze dan met next/script en afterInteractive. strategy="lazyOnload" schrijft geen preload (Next 16.4) en wordt tegengehouden tot de toestemming, maar alleen voor tags die automatische blokkering herkent (plan Business), dus geef de voorkeur aan 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>
  );
}

Met een Content Security Policy geeft u uw nonce door aan ConsentlScript (nonce={nonce}); Consentl behoudt die wanneer het de tag uitvoert.

Waarom de loader niet inline afdrukken?

Een eerdere versie van deze pagina haalde de loader op de server op en drukte hem inline af. In de App Router verstuurt dat de loader twee keer bij elke volledige paginalading, omdat React de inhoud van het <script> van een Server Component ook naar de paginagegevens kopieert, en statisch gebouwde pagina's de kopie die tijdens de build is opgehaald, behouden totdat ze opnieuw worden gevalideerd. Op dezelfde testpagina:

Configuratie HTML van de pagina Consentl-loader
Gewone scripttag (aanbevolen) 2,2 KB gzipped 9,5 KB gzipped, één verzoek, 5 minuten in de cache en gedeeld door alle pagina's
Inline loader 15,2 KB gzipped, bij elke volledige paginalading twee keer in de pagina

De gewone tag kost één verzoek aan api.consentl.com voordat de pagina wordt gerenderd. Met elke andere tag in ConsentlScript (of Google Tag Manager) staat er niets in de pagina dat de browser zou kunnen downloaden voordat Consentl wordt uitgevoerd.

next/script met strategy="beforeInteractive" wordt uitgegeven als een preload plus een inline wachtrij-item; Next voegt het echte <script> vóór de hydratatie in, na inline en async scripts die al in de server-rendered HTML staan.

Pages Router (pages/_document.tsx)

In de Pages Router wordt beforeInteractive met defer na de CSS-links gerenderd. Gebruik in plaats daarvan een gewoon script:

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>
  );
}

Navigatie aan de clientkant

Er is niets extra's nodig: de runtime blijft geladen, de keuze staat in de first-party cookie consentl en links "Cookie-instellingen" werken op elke route. Controleer voor uw eigen code de toestemming met window.Consentl?.allowed("analytics") en het event consentl:change, en start dingen maar één keer (zie Elke website).

Controleren of het werkt

Gebruik op een pagina View Source (Paginabron weergeven, niet de elementinspector): na de eigen /_next/static/chunks/…-scripts van Next moet Consentl vóór elk ander script komen, er is geen <link rel="preload"> voor een trackingtag en uw tags zijn type="text/plain". Open daarna de site in een privévenster en controleer de banner en het tabblad Network (Netwerk) zoals beschreven in Elke website: geen verzoek aan een tracker voordat u accepteert.

Beperkingen

  • async-tags die in de layout staan, worden door React boven Consentl geplaatst; gebruik in plaats daarvan ConsentlScript.
  • next/script met afterInteractive laat de browser de tag vóór de toestemming downloaden.
  • Met beforeInteractive worden scripts die al in de server-rendered HTML staan, vóór Consentl uitgevoerd.
  • Een taalwissel aan de clientkant laadt de banner niet opnieuw; de nieuwe taal geldt bij de volgende volledige paginalading.
  • Als u een Content Security Policy verstuurt, sta dan https://api.consentl.com toe (zie Elke website).

Vastgelopen? Schrijf naar support@consentl.com — wij antwoorden op werkdagen in het Engels.