Cookie Consent pour Next.js, Gatsby et les sites statiques : guide d’intégration pour développeurs
Le problème du consentement sur les sites statiques
Les frameworks JavaScript modernes comme Next.js, Gatsby et Nuxt.js ont introduit un changement de paradigme dans la façon dont les pages web sont construites et livrées. Les pages sont pré-rendues au moment du build ou sur le serveur, puis hydratées côté client. Cela crée un défi unique pour le cookie consent : la bannière de consentement doit être prête avant l’exécution de tout script de tracking, mais la page elle-même peut déjà être rendue et mise en cache en edge.
Les CMP traditionnels ont été conçus pour des pages PHP rendues côté serveur ou de simples pages HTML où le document se charge linéairement de haut en bas. Dans un monde de frameworks avec découpage de code, chargement paresseux (lazy loading) et rendu côté serveur en streaming, ces hypothèses ne tiennent plus. Obtenir un consentement correct dans ces environnements nécessite de comprendre le pipeline de rendu.
Pourquoi le timing compte plus que vous ne le pensez
Dans une page HTML standard, placer un script de CMP dans le <head> avant les autres scripts est simple. Dans Next.js App Router ou Gatsby, la situation est plus complexe :
- Le HTML pré-rendu arrive en premier : Le navigateur reçoit un HTML complet depuis le CDN ou le serveur. Si des scripts inline ou des tags tiers sont intégrés dans ce HTML, ils peuvent s’exécuter avant que votre logique de consentement ne soit chargée.
- Gap d’hydratation : L’hydratation React se produit après que le HTML a été peint. Si votre composant de consentement est un composant React, il n’existe pas dans un état fonctionnel tant que l’hydratation n’est pas terminée. Pendant ce gap, les tags Google ou les scripts d’analytics peuvent se déclencher sans consentement.
- Complications liées au cache edge : Si vous utilisez ISR (Incremental Static Regeneration) ou des fonctions edge, le HTML est mis en cache. Vous ne pouvez pas injecter dynamiquement une logique dépendante du consentement dans un HTML mis en cache sans mécanisme côté client.
Le principe fondamental est le suivant : le consentement doit être établi au niveau des scripts, pas au niveau des composants. Un composant React qui rend une bannière de consentement arrive trop tard s’il ne devient interactif qu’après l’hydratation.
Intégration avec Next.js App Router
Next.js 13+ avec l’App Router a introduit une nouvelle façon de gérer les scripts. Voici l’approche recommandée pour l’intégration du consentement :
Étape 1 : charger le script de CMP dans le layout racine
Utilisez le composant Script de Next.js avec la stratégie beforeInteractive dans votre layout.tsx racine. Cela indique à Next.js d’injecter le script dans le document HTML initial, avant le début de l’hydratation :
La stratégie beforeInteractive est critique. La stratégie par défaut afterInteractive charge les scripts après l’hydratation, ce qui est trop tard pour le consentement. Avec beforeInteractive, le script de CMP est inclus dans le HTML rendu côté serveur et s’exécute au chargement de la page.
Étape 2 : définir le consentement par défaut avant les tags Google
Avant votre snippet Google Tag Manager ou gtag.js, incluez un script inline qui définit les états de consentement par défaut. Cela garantit que même si GTM se charge avant l’apparition de la bannière CMP, il respecte les refus par défaut :
Ce script inline doit être placé dans le <head> de votre layout racine, avant les scripts CMP et GTM. Dans Next.js, vous pouvez utiliser une balise <script> classique à l’intérieur de l’élément <head> de votre layout à cet effet.
Étape 3 : gérer les changements de route
Dans une navigation de type single-page application, le script de CMP se charge une fois, mais les changements de route ne déclenchent pas un rechargement complet de la page. Votre CMP doit persister à travers les navigations côté client. FlexyConsent gère cela automatiquement : une fois chargé, il reste actif sur tous les changements de route sans réinitialisation.
Intégration avec Next.js Pages Router
Pour les projets qui utilisent encore le Pages Router, l’approche est similaire mais utilise _document.tsx au lieu du layout racine. Placez le script de CMP dans le composant <Head> de votre classe Document personnalisée. La stratégie beforeInteractive fonctionne de la même manière dans le Pages Router.
La différence clé est que _document.tsx est rendu uniquement côté serveur, de sorte que toute logique de consentement ici est garantie de se trouver dans le payload HTML initial.
Intégration des sites statiques Gatsby
Gatsby génère un HTML entièrement statique au moment du build. Il n’y a pas de rendu côté serveur à la requête, ce qui simplifie certains aspects mais en complique d’autres :
- Utilisez
gatsby-ssr.tsxpour injecter le script de CMP dans le<head>de chaque page. L’APIonRenderBodyvous permet d’ajouter des scripts dans le head qui seront présents dans chaque fichier HTML statique. - Évitez les plugins Gatsby qui chargent le consentement en lazy-load : Certains plugins communautaires encapsulent le consentement dans des composants React qui ne se montent qu’après l’hydratation. Cela crée le gap de timing évoqué plus haut.
- Placez les valeurs par défaut du consentement en inline : Utilisez
setHeadComponentsdansgatsby-ssr.tsxpour ajouter un script inline définissant les états de consentement par défaut. Ce script sera dans le HTML statique et s’exécutera immédiatement.
L’approche de build de Gatsby signifie que chaque fichier HTML sur votre CDN inclura le script de consentement. C’est en réalité idéal : il n’y a aucune logique serveur susceptible d’échouer ou d’être mal mise en cache.
Considérations pour Nuxt.js
Nuxt.js (basé sur Vue) a ses propres patterns. Dans Nuxt 3, utilisez le composable useHead ou la configuration d’app head dans nuxt.config.ts pour ajouter globalement le script de CMP. Nuxt prend en charge une option body: false (qui place les scripts dans le head) et un attribut async pour un chargement non bloquant.
Pour le mode rendu côté serveur de Nuxt, le même principe s’applique : le script de CMP doit se trouver dans la réponse HTML initiale, et non être injecté dynamiquement par un composant Vue après le montage.
Éviter les décalages de mise en page
Les bannières de consentement sont tristement célèbres pour provoquer des Cumulative Layout Shift (CLS), un Core Web Vital qui affecte le classement SEO. Lorsqu’une bannière apparaît après le rendu de la page, elle pousse le contenu vers le bas ou le recouvre de manière inattendue.
Stratégies pour minimiser le CLS causé par les bannières de consentement :
- Utiliser une bannière positionnée en bas : Les bannières en bas de la fenêtre ne décalent pas le contenu de la page. C’est l’approche la plus respectueuse du CLS.
- Réserver de l’espace : Si vous devez utiliser une bannière en haut, réservez l’espace vertical dans votre CSS afin que la mise en page de la page tienne compte de la bannière avant son rendu.
- Éviter les modales en overlay au chargement : Les murs de consentement en plein écran qui apparaissent après le rendu de la page créent une instabilité de mise en page perçue. Si vous avez besoin d’un mur, rendez-le comme partie de l’état initial de la page.
- Charger le CMP de manière synchrone dans le head : Lorsque le CMP est chargé comme script bloquant le rendu dans le head, la bannière peut apparaître dès le premier paint plutôt que de surgir plus tard.
L’approche agnostique de FlexyConsent vis-à-vis des frameworks
FlexyConsent a été conçu pour fonctionner avec n’importe quel framework — ou sans framework — en opérant au niveau des scripts plutôt qu’au niveau des composants. Voici pourquoi cela compte :
- Un seul tag script async : Une seule balise
<script>dans le<head>suffit. Aucun package npm à installer, aucun wrapper spécifique à un framework, aucune configuration de build. - Les valeurs par défaut de consentement se déclenchent immédiatement : Le script définit les valeurs par défaut de Consent Mode V2 comme première action, avant tout callback ou manipulation du DOM. Cela signifie que les tags Google respectent le consentement dès la toute première milliseconde.
- Aucune dépendance au DOM��: La logique de consentement n’attend pas que React, Vue ou Svelte s’hydrate. Elle fonctionne indépendamment du cycle de vie du framework.
- Fonctionne avec SSG, SSR, ISR et CSR : Parce qu’il s’agit d’un simple script, il fonctionne de manière identique que la page soit générée statiquement, rendue côté serveur, régénérée de façon incrémentale ou rendue côté client.
Astuce développeur : Le test le plus simple pour vérifier une intégration correcte du CMP consiste à ouvrir l’onglet Réseau de votre navigateur, filtrer par domaines Google, puis recharger la page. Aucune requête Google ne doit partir avant que la commande de consentement par défaut n’apparaisse dans la console. Si c’est le cas, votre CMP se charge trop tard.
Le plan gratuit de FlexyConsent prend en charge un nombre illimité de pages vues et fonctionne avec Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix et du simple HTML. L’intégration est la même partout : une balise script, correctement placée.