Cookie Consent pentru Next.js, Gatsby și site-uri statice: Ghid de integrare pentru dezvoltatori
Problema consimțământului pe site-uri statice
Framework-urile moderne JavaScript precum Next.js, Gatsby și Nuxt.js au introdus o schimbare de paradigmă în modul în care paginile web sunt construite și livrate. Paginile sunt pre-randate la build sau pe server, apoi sunt hidratate pe client. Acest lucru creează o provocare unică pentru cookie consent: bannerul de consimțământ trebuie să fie gata înainte ca orice script de tracking să fie executat, dar pagina în sine poate fi deja randată și cache-uită la edge.
CMP-urile tradiționale au fost concepute pentru pagini PHP randate pe server sau pentru pagini HTML simple, unde documentul se încarcă liniar de sus în jos. Într-o lume a framework-urilor, cu code splitting, lazy loading și streaming server-side rendering, aceste presupuneri nu mai sunt valabile. Obținerea unui consimțământ corect în aceste medii necesită înțelegerea pipeline-ului de randare.
De ce momentul contează mai mult decât crezi
Într-o pagină HTML standard, plasarea unui script CMP în <head> înaintea altor scripturi este simplă. În Next.js App Router sau Gatsby, situația este mai complexă:
- HTML pre-randat ajunge primul: Browserul primește HTML complet de la CDN sau server. Dacă orice script inline sau tag-uri third-party sunt încorporate în acel HTML, ele pot fi executate înainte ca logica ta de consimțământ să se încarce.
- Gap de hidratare: Hidratarea React are loc după ce HTML-ul este afișat. Dacă componenta ta de consimțământ este o componentă React, ea nu există într-o stare funcțională până când hidratarea nu este completă. În acest interval, tag-urile Google sau scripturile de analytics pot porni fără consimțământ.
- Complicații cu edge caching: Dacă folosești ISR (Incremental Static Regeneration) sau edge functions, HTML-ul este cache-uit. Nu poți injecta dinamic logică dependentă de consimțământ în HTML-ul cache-uit fără un mecanism pe client.
Principiul de bază este acesta: consimțământul trebuie stabilit la nivel de script, nu la nivel de componentă. O componentă React care randă un banner de consimțământ este prea târziu dacă devine interactivă abia după hidratare.
Integrare cu Next.js App Router
Next.js 13+ cu App Router a introdus un mod nou de gestionare a scripturilor. Iată abordarea recomandată pentru integrarea consimțământului:
Pasul 1: Încarcă scriptul CMP în root layout
Folosește componenta Script din Next.js cu strategia beforeInteractive în layout.tsx de la rădăcină. Acest lucru îi spune lui Next.js să injecteze scriptul în documentul HTML inițial, înainte să înceapă hidratarea:
Strategia beforeInteractive este critică. Strategia implicită afterInteractive încarcă scripturile după hidratare, ceea ce este prea târziu pentru consimțământ. Cu beforeInteractive, scriptul CMP este inclus în HTML-ul randat pe server și se execută pe măsură ce pagina se încarcă.
Pasul 2: Setează consimțământul implicit înainte de tag-urile Google
Înainte de snippet-ul Google Tag Manager sau gtag.js, include un script inline care setează stările implicite de consimțământ. Acest lucru asigură că, chiar dacă GTM se încarcă înainte ca bannerul CMP să apară, el respectă valorile implicite de refuz:
Acest script inline ar trebui plasat în <head>-ul root layout-ului, înainte de scripturile CMP și GTM. În Next.js, poți folosi un tag obișnuit <script> în interiorul elementului <head> al layout-ului pentru acest scop.
Pasul 3: Gestionează schimbările de rută
În navigarea tip single-page application, scriptul CMP se încarcă o singură dată, dar schimbările de rută nu declanșează un reload complet al paginii. CMP-ul tău trebuie să persiste pe navigările pe client. FlexyConsent gestionează acest lucru automat — odată încărcat, rămâne activ pe toate schimbările de rută fără re-initializare.
Integrare cu Next.js Pages Router
Pentru proiectele care folosesc încă Pages Router, abordarea este similară, dar folosește _document.tsx în loc de root layout. Plasează scriptul CMP în componenta <Head> a clasei tale custom Document. Strategia beforeInteractive funcționează la fel în Pages Router.
Diferența cheie este că _document.tsx este randat doar pe server, astfel încât orice logică de consimțământ de aici este garantat prezentă în payload-ul HTML inițial.
Integrare statică în Gatsby
Gatsby generează HTML complet static la build. Nu există server-side rendering la momentul request-ului, ceea ce simplifică unele aspecte, dar complică altele:
- Folosește
gatsby-ssr.tsxpentru a injecta scriptul CMP în<head>-ul fiecărei pagini. API-ulonRenderBodyîți permite să adaugi scripturi în head care vor fi prezente în fiecare fișier HTML static. - Evită plugin-urile Gatsby care încarcă consimțământul lazy: Unele plugin-uri din comunitate înfășoară consimțământul în componente React care se montează doar după hidratare. Acest lucru creează gap-ul de timing discutat mai devreme.
- Plasează valorile implicite de consimțământ inline: Folosește
setHeadComponentsîngatsby-ssr.tsxpentru a adăuga un script inline care setează stările implicite de consimțământ. Acest script va fi în HTML-ul static și se va executa imediat.
Abordarea de build-time a lui Gatsby înseamnă că fiecare fișier HTML de pe CDN va include scriptul de consimțământ. Acest lucru este de fapt ideal — nu există logică de server care să poată eșua sau să fie cache-uită incorect.
Considerații pentru Nuxt.js
Nuxt.js (bazat pe Vue) are propriile sale pattern-uri. În Nuxt 3, folosește composable-ul useHead sau configurația app head din nuxt.config.ts pentru a adăuga scriptul CMP global. Nuxt suportă opțiunea body: false (care plasează scripturile în head) și atributul async pentru încărcare non-blocantă.
Pentru modul de server-side rendering al lui Nuxt, se aplică același principiu: scriptul CMP trebuie s�� fie în răspunsul HTML inițial, nu injectat dinamic de o componentă Vue după mount.
Evitarea schimbărilor de layout
Banner-ele de consimțământ sunt notorii pentru cauzarea Cumulative Layout Shift (CLS), un Core Web Vital care afectează clasările SEO. Când un banner apare după ce pagina a fost randată, împinge conținutul în jos sau îl suprapune neașteptat.
Strategii pentru a minimiza CLS cauzat de banner-ele de consimțământ:
- Folosește un banner poziționat jos: Banner-ele din partea de jos a viewport-ului nu deplasează conținutul paginii. Aceasta este abordarea cea mai prietenoasă cu CLS.
- Rezervă spațiu: Dacă trebuie să folosești un banner sus, rezervă spațiul vertical în CSS astfel încât layout-ul paginii să țină cont de banner înainte ca acesta să fie randat.
- Evită overlay-urile de tip modal la încărcare: Pereții de consimțământ full-screen care apar după ce pagina a fost randată creează o instabilitate percepută a layout-ului. Dacă ai nevoie de un perete, randă-l ca parte a stării inițiale a paginii.
- Încarcă CMP-ul sincron în head: Când CMP-ul este încărcat ca script blocant de randare în head, bannerul poate apărea ca parte a paint-ului inițial, în loc să apară ulterior.
Abordarea agnostică de framework a FlexyConsent
FlexyConsent a fost conceput să funcționeze cu orice framework — sau fără niciun framework — operând la nivel de script, nu la nivel de componentă. Iată de ce contează acest lucru:
- Un singur tag de script async: Este suficient un singur tag
<script>în<head>. Fără pachete npm de instalat, fără wrapper-e specifice framework-ului, fără configurare de build. - Valorile implicite de consimțământ pornesc imediat: Scriptul setează valorile implicite pentru Consent Mode V2 ca primă acțiune, înainte de orice callback sau manipulare a DOM-ului. Asta înseamnă că tag-urile Google respectă consimțământul din prima milisecundă.
- Fără dependență de DOM: Logica de consimțământ nu așteaptă ca React, Vue sau Svelte să hidrateze. Ea operează independent de ciclul de viață al framework-ului.
- Funcționează cu SSG, SSR, ISR și CSR: Pentru că este un script simplu, funcționează identic indiferent dacă pagina a fost generată static, randată pe server, regenerată incremental sau randată pe client.
Sfat pentru dezvoltatori: Cel mai simplu test pentru o integrare corectă a CMP este să deschizi tab-ul Network din browser, să filtrezi după domeniile Google și să reîncarci pagina. Niciun request către Google nu ar trebui să pornească înainte ca comanda de consimțământ implicit să apară în consolă. Dacă pornesc, CMP-ul tău se încarcă prea târziu.
Planul gratuit FlexyConsent suportă pageview-uri nelimitate și funcționează cu Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix și HTML simplu. Integrarea este aceeași peste tot: un singur script tag, plasat corect.