Consentl com Next.js

Coloque o Consentl em primeiro lugar no <head> do layout raiz do App Router, carregue as outras tags com um componente ConsentlScript ou use o _document.

Loader em primeiro lugar no <head>
Sim

Para o bloqueio prévio e as predefinições do Google Consent Mode, o Consentl tem de ser executado antes de todas as outras tags, de forma síncrona (sem async, defer ou type="module"). No Next.js, isso significa uma tag <script> simples, e não next/script.

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

Guarde a chave do site numa variável de ambiente como NEXT_PUBLIC_CONSENTL_SITE_KEY. A chave é pública, pelo que expô-la ao navegador não é um problema. O Consent Mode exige o plano Pro ou superior e o bloqueio automático de rastreadores conhecidos o plano Business.

App Router (app/layout.tsx)

Testado com Next.js 16.4 e React 19.

O que o React 19 e o Next fazem às suas tags

  • O React trata cada <script async src> como um recurso e move-o para o topo do <head>, acima de um script síncrono simples, mesmo que o tenha escrito depois do Consentl. Os scripts próprios do Next também ficam em primeiro lugar.
  • next/script com strategy="afterInteractive" (e beforeInteractive) escreve na página um <link rel="preload"> para a tag. O navegador transfere a tag enquanto lê a página, antes do consentimento. O Consentl impede a sua execução, mas não esse pedido.

Os dados de página transmitidos em streaming pelo Next (self.__next_f) nunca são retidos pelo Consentl, seja qual for o seu conteúdo.

  1. O Consentl como <script src> simples, em primeiro lugar no <head>. O React não o move, está na página uma única vez e os visitantes recebem sempre as definições atuais do seu banner (o loader fica em cache durante 5 minutos).
  2. Todas as outras tags que têm de aguardar o consentimento (Google Analytics, Meta, TikTok…) através do pequeno componente ConsentlScript abaixo. Este escreve a tag como type="text/plain" com a respetiva categoria do Consentl: o React não a move, o navegador não a transfere e o Consentl executa-a uma vez após o consentimento, em todos os planos, também após a navegação do lado do cliente.
  3. Ou gira essas tags no Google Tag Manager (consulte Google Tag Manager).

As tags Google no modo avançado do Consent Mode (plano Pro e superiores) destinam-se a carregar antes do consentimento; se utilizar esse modo, carregue-as com next/script e afterInteractive. strategy="lazyOnload" não escreve preload (Next 16.4) e é retida até ao consentimento, mas apenas para as tags que o bloqueio automático reconhece (plano Business), pelo que deve preferir 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>
  );
}

Com uma Content Security Policy, passe o seu nonce a ConsentlScript (nonce={nonce}); o Consentl mantém-no quando executa a tag.

Porque não escrever o loader inline?

Uma versão anterior desta página obtinha o loader no servidor e escrevia-o inline. No App Router, isso envia o loader duas vezes em cada carregamento completo de página, porque o React também copia o conteúdo do <script> de um Server Component para os dados da página, e as páginas geradas estaticamente mantêm a cópia obtida no momento da compilação até serem revalidadas. Na mesma página de teste:

Configuração HTML da página Loader do Consentl
Tag script simples (recomendado) 2,2 KB gzipped 9,5 KB gzipped, um pedido, em cache durante 5 minutos e partilhado por todas as páginas
Loader inline 15,2 KB gzipped, em cada carregamento completo de página duas vezes na página

A tag simples custa um pedido a api.consentl.com antes de a página ser apresentada. Com todas as outras tags em ConsentlScript (ou no Google Tag Manager), não há nada na página que o navegador possa transferir antes de o Consentl ser executado.

next/script com strategy="beforeInteractive" é emitido como um preload mais uma entrada inline numa fila; o Next insere o verdadeiro <script> antes da hidratação, depois dos scripts inline e assíncronos que já estão no HTML gerado no servidor.

Pages Router (pages/_document.tsx)

No Pages Router, beforeInteractive é apresentado com defer depois das ligações CSS. Utilize um script simples:

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

Navegação do lado do cliente

Não é preciso mais nada: o runtime mantém-se carregado, a escolha fica no cookie próprio consentl e as ligações «Definições de cookies» funcionam em qualquer rota. No seu próprio código, verifique o consentimento com window.Consentl?.allowed("analytics") e o evento consentl:change, e inicie tudo apenas uma vez (consulte Qualquer site).

Verificar que funciona

Utilize View Source (e não o inspetor de elementos) numa página: depois dos scripts próprios do Next /_next/static/chunks/…, o Consentl tem de vir antes de qualquer outro script, não existe nenhum <link rel="preload"> para uma tag de rastreio e as suas tags são type="text/plain". Depois, abra o site numa janela privada e verifique o banner e o separador Network, conforme descrito em Qualquer site: nenhum pedido a um rastreador antes de aceitar.

Limitações

  • As tags async escritas no layout são movidas pelo React para cima do Consentl; utilize ConsentlScript em alternativa.
  • next/script com afterInteractive faz o navegador transferir a tag antes do consentimento.
  • Com beforeInteractive, os scripts que já estão no HTML gerado no servidor são executados antes do Consentl.
  • Uma mudança de idioma no cliente não recarrega o banner; o novo idioma aplica-se no próximo carregamento completo da página.
  • Se enviar uma Content Security Policy, permita https://api.consentl.com (consulte Qualquer site).

Com dificuldades? Escreva para support@consentl.com — respondemos em inglês nos dias úteis.