Cookie-toestemming voor Next.js, Gatsby en statische sites: een integratiegids voor developers
Het toestemmingsprobleem bij statische sites
Moderne JavaScript-frameworks zoals Next.js, Gatsby en Nuxt.js hebben een paradigmaverschuiving geïntroduceerd in hoe webpagina’s worden opgebouwd en geleverd. Pagina’s worden vooraf gerenderd tijdens de build of op de server en vervolgens op de client gehydrateerd. Dit creëert een unieke uitdaging voor cookie-toestemming: de toestemmingsbanner moet klaar zijn vóórdat tracking-scripts worden uitgevoerd, maar de pagina zelf kan al gerenderd en aan de edge gecachet zijn.
Traditionele CMP’s zijn ontworpen voor server-gerenderde PHP- of eenvoudige HTML-pagina’s, waar het document lineair van boven naar beneden laadt. In een framework-wereld met code splitting, lazy loading en streaming server-side rendering gaan die aannames niet meer op. Toestemming goed regelen in deze omgevingen vereist begrip van de rendering-pipeline.
Waarom timing belangrijker is dan je denkt
In een standaard HTML-pagina is het eenvoudig om een CMP-script in de <head> te plaatsen vóór andere scripts. In Next.js App Router of Gatsby is de situatie complexer:
- Vooraf gerenderde HTML komt eerst aan: De browser ontvangt volledige HTML van de CDN of server. Als er inline scripts of third-party tags in die HTML zijn ingesloten, kunnen die worden uitgevoerd vóórdat je toestemmingslogica laadt.
- Hydratatie-gap: React-hydratatie vindt plaats nadat de HTML is getekend. Als je toestemmingscomponent een React-component is, bestaat die pas functioneel nadat hydratatie is voltooid. Tijdens deze gap kunnen Google-tags of analytics-scripts afvuren zonder toestemming.
- Complicaties door edge-caching: Als je ISR (Incremental Static Regeneration) of edge functions gebruikt, wordt de HTML gecachet. Je kunt geen toestemmingsafhankelijke logica dynamisch in gecachete HTML injecteren zonder een client-side mechanisme.
De kernprincipes is dit: toestemming moet op scriptniveau worden vastgesteld, niet op componentniveau. Een React-component die een toestemmingsbanner rendert, is te laat als hij pas interactief wordt na hydratatie.
Integratie met Next.js App Router
Next.js 13+ met de App Router introduceerde een nieuwe manier om scripts af te handelen. Dit is de aanbevolen aanpak voor toestemmingsintegratie:
Stap 1: Laad het CMP-script in de root layout
Gebruik de Next.js-Script-component met de beforeInteractive-strategie in je root-layout.tsx. Dit vertelt Next.js om het script in het initiële HTML-document te injecteren, vóórdat hydratatie begint:
De beforeInteractive-strategie is cruciaal. De standaardstrategie afterInteractive laadt scripts na hydratatie, wat te laat is voor toestemming. Met beforeInteractive wordt het CMP-script opgenomen in de server-gerenderde HTML en uitgevoerd terwijl de pagina laadt.
Stap 2: Stel standaardtoestemming in vóór Google-tags
Voeg vóór je Google Tag Manager- of gtag.js-snippet een inline script toe dat standaard toestemmingsstatussen instelt. Dit zorgt ervoor dat zelfs als GTM laadt vóórdat de CMP-banner verschijnt, het de geweigerde defaults respecteert:
Dit inline script moet in de <head> van je root layout worden geplaatst, vóór de CMP- en GTM-scripts. In Next.js kun je hiervoor een gewone <script>-tag gebruiken binnen het <head>-element van je layout.
Stap 3: Afhandelen van routewijzigingen
Bij single-page application-navigatie wordt het CMP-script één keer geladen, maar routewijzigingen triggeren geen volledige paginareload. Je CMP moet blijven werken over client-side navigaties heen. FlexyConsent handelt dit automatisch af — eenmaal geladen blijft het actief bij alle routewijzigingen zonder herinitialisatie.
Integratie met Next.js Pages Router
Voor projecten die nog de Pages Router gebruiken, is de aanpak vergelijkbaar maar gebruik je _document.tsx in plaats van de root layout. Plaats het CMP-script in de <Head>-component van je custom Document-klasse. De beforeInteractive-strategie werkt op dezelfde manier in de Pages Router.
Het belangrijkste verschil is dat _document.tsx alleen op de server rendert, zodat alle toestemmingslogica hier gegarandeerd in de initiële HTML-payload zit.
Gatsby-integratie voor statische sites
Gatsby genereert volledig statische HTML tijdens de build. Er is geen server-side rendering op request-niveau, wat sommige aspecten vereenvoudigt maar andere bemoeilijkt:
- Gebruik
gatsby-ssr.tsxom het CMP-script in de<head>van elke pagina te injecteren. DeonRenderBody-API laat je scripts toevoegen aan de head die in elk statisch HTML-bestand aanwezig zullen zijn. - Vermijd Gatsby-plugins die toestemming lazy-loaden: Sommige community-plugins wikkelen toestemming in React-componenten die pas mounten na hydratatie. Dit creëert de eerder besproken timing-gap.
- Plaats toestemmingsdefaults inline: Gebruik
setHeadComponentsingatsby-ssr.tsxom een inline script toe te voegen dat standaard toestemmingsstatussen instelt. Dit script staat in de statische HTML en wordt direct uitgevoerd.
Door Gatsby’s build-time aanpak bevat elk HTML-bestand op je CDN het toestemmingsscript. Dit is eigenlijk ideaal — er is geen serverlogica die kan falen of verkeerd cachen.
Nuxt.js-overwegingen
Nuxt.js (Vue-based) heeft zijn eigen patronen. In Nuxt 3 gebruik je de useHead-composable of de nuxt.config.ts-app-headconfiguratie om het CMP-script globaal toe te voegen. Nuxt ondersteunt een body: false-optie (die scripts in de head plaatst) en een async-attribuut voor niet-blokkerende loading.
Voor de server-side rendering-modus van Nuxt geldt hetzelfde principe: het CMP-script moet in de initiële HTML-respons zitten, niet dynamisch worden geïnjecteerd door een Vue-component na mount.
Layout shift voorkomen
Toestemmingsbanners staan erom bekend Cumulative Layout Shift (CLS) te veroorzaken, een Core Web Vital die invloed heeft op SEO-rankings. Wanneer een banner na het renderen van de pagina in beeld springt, duwt hij content naar beneden of overlapt die onverwacht.
Strategieën om CLS door toestemmingsbanners te minimaliseren:
- Gebruik een banner onderaan: Banners onderaan de viewport verschuiven de paginacontent niet. Dit is de meest CLS-vriendelijke aanpak.
- Reserveer ruimte: Als je een banner bovenaan moet gebruiken, reserveer dan verticale ruimte in je CSS zodat de paginalayout al rekening houdt met de banner vóórdat hij rendert.
- Vermijd modale overlays bij load: Fullscreen toestemmingswalls die verschijnen nadat de pagina is gerenderd, veroorzaken een gevoel van layout-instabiliteit. Als je een wall nodig hebt, render die dan als onderdeel van de initiële paginastatus.
- Laad de CMP synchroon in de head: Wanneer de CMP als render-blokkerend script in de head wordt geladen, kan de banner deel uitmaken van de eerste paint in plaats van later in te poppen.
Het framework-agnostische aanpak van FlexyConsent
FlexyConsent is ontworpen om met elk framework — of helemaal geen framework — te werken door op scriptniveau in plaats van componentniveau te opereren. Dit is waarom dat ertoe doet:
- Eén async script-tag: Eén
<script>-tag in de<head>is alles wat nodig is. Geen npm-packages om te installeren, geen framework-specifieke wrappers, geen buildconfiguratie. - Toestemmingsdefaults vuren onmiddellijk: Het script stelt Consent Mode V2-defaults in als eerste actie, vóór elke callback of DOM-manipulatie. Dit betekent dat Google-tags toestemming respecteren vanaf de allereerste milliseconde.
- Geen DOM-afhankelijkheid: De toestemmingslogica wacht niet op hydratatie van React, Vue of Svelte. Ze werkt onafhankelijk van de framework-lifecycle.
- Werkt met SSG, SSR, ISR en CSR: Omdat het een gewoon script is, functioneert het identiek, of de pagina nu statisch is gegenereerd, server-gerenderd, incrementeel geregenereerd of client-side gerenderd.
Developer-tip: De eenvoudigste test voor correcte CMP-integratie is om het Network-tabblad van je browser te openen, te filteren op Google-domeinen en de pagina te herladen. Er mogen geen Google-requests afvuren vóórdat het toestemmingsdefault-commando in de console verschijnt. Als dat wel gebeurt, laadt je CMP te laat.
Het gratis plan van FlexyConsent ondersteunt onbeperkte pageviews en werkt met Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix en pure HTML. De integratie is in al deze gevallen hetzelfde: één script-tag, op de juiste plek geplaatst.