Consentl z Next.js
Umieść Consentl jako pierwszy w <head> głównego layoutu App Router, ładuj inne tagi przez mały komponent ConsentlScript lub użyj _document w Pages Router.
- Skrypt ładujący jako pierwszy w <head>
- Tak
Na potrzeby wstępnego blokowania i domyślnych wartości Google Consent Mode Consentl musi uruchamiać
się przed każdym innym tagiem, synchronicznie (bez async, defer ani type="module").
W Next.js oznacza to zwykły tag <script>, a nie next/script.
<script src="https://api.consentl.com/v1/loader.js?k=SITE_KEY&l=LANG"></script>
Klucz witryny należy przechowywać w zmiennej środowiskowej, np. NEXT_PUBLIC_CONSENTL_SITE_KEY.
Klucz jest publiczny, więc udostępnienie go przeglądarce nie stanowi problemu. Consent Mode wymaga
planu Pro lub wyższego, a automatyczne blokowanie znanych narzędzi śledzących – planu Business.
App Router (app/layout.tsx)
Przetestowano z Next.js 16.4 i React 19.
Co React 19 i Next robią z tagami
- React traktuje każdy
<script async src>jako zasób i przenosi go na początek<head>, powyżej zwykłego synchronicznego skryptu, nawet jeżeli zapisano go po Consentl. Własne skrypty Next również znajdują się na początku. next/scriptzestrategy="afterInteractive"(orazbeforeInteractive) zapisuje na stronie<link rel="preload">dla tagu. Przeglądarka pobiera tag podczas odczytywania strony, przed wyrażeniem zgody. Consentl uniemożliwia jego uruchomienie, ale nie to żądanie.
Strumieniowane dane strony Next (self.__next_f) nigdy nie są wstrzymywane przez Consentl, niezależnie
od ich zawartości.
Zalecana konfiguracja
- Consentl jako zwykły
<script src>, pierwszy w<head>. React go nie przenosi, występuje na stronie raz, a odwiedzający zawsze otrzymują aktualne ustawienia banera (skrypt ładujący jest przechowywany w pamięci podręcznej przez 5 minut). - Każdy inny tag, który musi czekać na zgodę (Google Analytics, Meta, TikTok…), przez mały komponent
ConsentlScriptponiżej. Zapisuje on tag jakotype="text/plain"z jego kategorią Consentl: React go nie przenosi, przeglądarka go nie pobiera, a Consentl uruchamia go raz po wyrażeniu zgody, w każdym planie, również po nawigacji po stronie klienta. - Można też zarządzać tymi tagami w Google Tag Manager (zob. Google Tag Manager).
Tagi Google w trybie zaawansowanym (advanced) Consent Mode (plan Pro i wyższe) z założenia ładują
się przed wyrażeniem zgody; w tym trybie należy ładować je za pomocą next/script
i afterInteractive. strategy="lazyOnload" nie zapisuje preload (Next 16.4) i jest wstrzymywane do
czasu wyrażenia zgody, ale tylko w przypadku tagów rozpoznawanych przez automatyczne blokowanie (plan
Business), dlatego lepiej używać 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>
);
}
Przy Content Security Policy należy przekazać nonce do ConsentlScript (nonce={nonce}); Consentl
zachowuje go, uruchamiając tag.
Dlaczego nie wstawiać skryptu ładującego bezpośrednio w kod strony?
Wcześniejsza wersja tej strony pobierała skrypt ładujący na serwerze i wstawiała go bezpośrednio
w kod strony (inline). W App Router powoduje to wysyłanie skryptu ładującego dwukrotnie przy każdym
pełnym załadowaniu strony, ponieważ React kopiuje również zawartość <script> komponentu
serwerowego do danych strony, a strony budowane statycznie zachowują kopię pobraną podczas budowania
do czasu ich ponownej walidacji. Na tej samej stronie testowej:
| Konfiguracja | HTML strony | Skrypt ładujący Consentl |
|---|---|---|
| Zwykły tag script (zalecane) | 2,2 KB po kompresji gzip | 9,5 KB po kompresji gzip, jedno żądanie, w pamięci podręcznej przez 5 minut i współdzielony przez wszystkie strony |
| Skrypt ładujący inline | 15,2 KB po kompresji gzip, przy każdym pełnym załadowaniu strony | dwukrotnie na stronie |
Zwykły tag kosztuje jedno żądanie do api.consentl.com przed wyrenderowaniem strony. Gdy każdy inny
tag znajduje się w ConsentlScript (lub w Google Tag Manager), na stronie nie ma niczego, co
przeglądarka mogłaby pobrać przed uruchomieniem Consentl.
Niezalecane: beforeInteractive
next/script ze strategy="beforeInteractive" jest emitowany jako preload i wpis w kolejce inline;
Next wstawia właściwy <script> przed hydratacją, po skryptach inline i async, które już są
w HTML wyrenderowanym na serwerze.
Pages Router (pages/_document.tsx)
W Pages Router beforeInteractive jest renderowany z defer po linkach CSS. Zamiast tego należy użyć
zwykłego skryptu:
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>
);
}
Nawigacja po stronie klienta
Nie trzeba niczego dodawać: środowisko uruchomieniowe pozostaje załadowane, wybór jest przechowywany
we własnym pliku cookie consentl, a linki „Ustawienia plików cookie” działają na każdej trasie.
We własnym kodzie należy sprawdzać zgodę za pomocą window.Consentl?.allowed("analytics")
i zdarzenia consentl:change, a funkcje uruchamiać tylko raz (zob.
Każda strona internetowa).
Sprawdzenie działania
Na stronie należy użyć View Source (a nie inspektora elementów): po własnych skryptach Next
/_next/static/chunks/… Consentl musi znajdować się przed każdym innym skryptem, nie ma
<link rel="preload"> dla tagu śledzącego, a Państwa tagi mają type="text/plain". Następnie należy
otworzyć witrynę w oknie prywatnym i sprawdzić baner oraz kartę Network zgodnie z opisem w poradniku
Każda strona internetowa: brak żądań do narzędzi śledzących przed
akceptacją.
Ograniczenia
- Tagi
asynczapisane w layoucie są przenoszone przez React powyżej Consentl; zamiast nich należy używaćConsentlScript. next/scriptzafterInteractivepowoduje pobranie tagu przez przeglądarkę przed wyrażeniem zgody.- Przy
beforeInteractiveskrypty, które już są w HTML wyrenderowanym na serwerze, uruchamiają się przed Consentl. - Zmiana języka po stronie klienta nie przeładowuje banera; nowy język obowiązuje od następnego pełnego załadowania strony.
- Jeżeli wysyłają Państwo nagłówek Content Security Policy, należy zezwolić na
https://api.consentl.com(zob. Każda strona internetowa).
Powiązane poradniki
Potrzebują Państwo pomocy? Prosimy pisać na adres support@consentl.com — odpowiadamy po angielsku w dni robocze.