Consentl con Next.js

Inserisca Consentl per primo nel <head> del layout radice dell'App Router, carichi gli altri tag con un componente ConsentlScript o usi il _document del Pages Router.

Loader per primo nel <head>
Sì

Per il blocco preventivo e i valori predefiniti di Google Consent Mode, Consentl deve essere eseguito prima di ogni altro tag, in modo sincrono (niente async, defer o type="module"). In Next.js ciò significa un tag <script> semplice, non next/script.

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

Memorizzi la chiave del sito in una variabile d'ambiente come NEXT_PUBLIC_CONSENTL_SITE_KEY. La chiave è pubblica, quindi esporla al browser non è un problema. Consent Mode richiede il piano Pro o superiore, il blocco automatico dei tracker noti il piano Business.

App Router (app/layout.tsx)

Testato con Next.js 16.4 e React 19.

Che cosa fanno React 19 e Next ai suoi tag

  • React tratta ogni <script async src> come una risorsa e lo sposta in cima al <head>, sopra uno script sincrono semplice, anche se lo ha scritto dopo Consentl. Anche gli script di Next vengono per primi.
  • next/script con strategy="afterInteractive" (e beforeInteractive) scrive nella pagina un <link rel="preload"> per il tag. Il browser scarica il tag mentre legge la pagina, prima del consenso. Consentl ne impedisce l'esecuzione, ma non quella richiesta.

I dati di pagina trasmessi in streaming da Next (self.__next_f) non vengono mai trattenuti da Consentl, qualunque cosa contengano.

  1. Consentl come <script src> semplice, per primo nel <head>. React non lo sposta, è presente una sola volta nella pagina e i visitatori ricevono sempre le impostazioni attuali del banner (il loader viene memorizzato nella cache per 5 minuti).
  2. Ogni altro tag che deve attendere il consenso (Google Analytics, Meta, TikTok…) tramite il piccolo componente ConsentlScript qui sotto. Scrive il tag come type="text/plain" con la sua categoria Consentl: React non lo sposta, il browser non lo scarica e Consentl lo esegue una volta dopo il consenso, in ogni piano, anche dopo la navigazione lato client.
  3. Oppure gestisca questi tag in Google Tag Manager (veda Google Tag Manager).

I tag Google in modalità avanzata di Consent Mode (dal piano Pro in su) sono pensati per caricarsi prima del consenso; se usa questa modalità, li carichi con next/script e afterInteractive. strategy="lazyOnload" non scrive alcun preload (Next 16.4) e viene trattenuto fino al consenso, ma solo per i tag che il blocco automatico riconosce (piano Business), quindi preferisca 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>
  );
}

Con una Content Security Policy, passi il suo nonce a ConsentlScript (nonce={nonce}); Consentl lo mantiene quando esegue il tag.

Perché non stampare il loader inline?

Una versione precedente di questa pagina recuperava il loader sul server e lo stampava inline. Nell'App Router ciò invia il loader due volte a ogni caricamento completo della pagina, perché React copia anche il contenuto dello <script> di un Server Component nei dati di pagina, e le pagine generate staticamente mantengono la copia recuperata al momento della build finché non vengono riconvalidate. Sulla stessa pagina di test:

Configurazione HTML della pagina Loader di Consentl
Tag script semplice (consigliato) 2,2 KB compressi con gzip 9,5 KB compressi con gzip, una richiesta, in cache per 5 minuti e condiviso da tutte le pagine
Loader inline 15,2 KB compressi con gzip, a ogni caricamento completo della pagina presente due volte nella pagina

Il tag semplice costa una richiesta a api.consentl.com prima che la pagina venga visualizzata. Con ogni altro tag in ConsentlScript (o in Google Tag Manager), nella pagina non c'è nulla che il browser possa scaricare prima che Consentl venga eseguito.

next/script con strategy="beforeInteractive" viene emesso come un preload più una voce di coda inline; Next inserisce lo <script> effettivo prima dell'idratazione, dopo gli script inline e asincroni già presenti nell'HTML generato dal server.

Pages Router (pages/_document.tsx)

Nel Pages Router, beforeInteractive viene generato con defer dopo i link CSS. Usi invece uno script semplice:

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

Navigazione lato client

Non serve altro: il runtime resta caricato, la scelta risiede nel cookie di prima parte consentl e i link «Impostazioni dei cookie» funzionano su qualsiasi route. Per il suo codice, verifichi il consenso con window.Consentl?.allowed("analytics") e l'evento consentl:change, e avvii le cose una sola volta (veda Qualsiasi sito web).

Verificare che funzioni

Usi View Source (non l'ispettore degli elementi) su una pagina: dopo gli script di Next /_next/static/chunks/…, Consentl deve precedere ogni altro script, non c'è alcun <link rel="preload"> per un tag di tracciamento e i suoi tag sono type="text/plain". Poi apra il sito in una finestra privata e controlli il banner e la scheda Network come descritto in Qualsiasi sito web: nessuna richiesta a un tracker prima che lei accetti.

Limitazioni

  • I tag async scritti nel layout vengono spostati da React sopra Consentl; usi invece ConsentlScript.
  • next/script con afterInteractive fa sì che il browser scarichi il tag prima del consenso.
  • Con beforeInteractive, gli script già presenti nell'HTML generato dal server vengono eseguiti prima di Consentl.
  • Un cambio di lingua sul client non ricarica il banner; la nuova lingua si applica al successivo caricamento completo della pagina.
  • Se invia una Content Security Policy, consenta https://api.consentl.com (veda Qualsiasi sito web).

Serve aiuto? Scriva a support@consentl.com: rispondiamo in inglese nei giorni lavorativi.