# Gérer le consentement aux cookies dans les applications monopages

> Consentement aux cookies dans React, Vue et les autres SPA : état partagé, suivi des changements de route, hydratation SSR, contenus intégrés et retrait immédiat.

Source: https://www.cookiesh.eu/fr/blog/consent-in-single-page-apps  
Published: 2026-08-11

## Key takeaways

- Dans une application monopage, le consentement fait partie de l'état de l'application : stockez-le une seule fois, partagez-le avec chaque composant et réagissez aux changements sans rechargement.
- Réglez par défaut chaque catégorie non essentielle sur refusé, afin que le code qui s'exécute avant le chargement du choix enregistré ne puisse pas laisser fuiter de données.
- N'envoyez des pages vues virtuelles lors des changements de route que si le visiteur a accepté la mesure d'audience.
- Lisez le cookie de consentement pendant le rendu serveur, ou affichez le bandeau uniquement côté client, pour éviter les incohérences d'hydratation et le clignotement du bandeau.
- Retirer son consentement doit être aussi simple que le donner, et recharger la page est souvent le moyen le plus propre d'arrêter les scripts déjà en cours d'exécution.

Dans une application monopage (SPA), la page n'est jamais entièrement rechargée : le consentement aux cookies doit donc faire partie de l'état de l'application, et non d'un script qui s'exécute une seule fois. Enregistrez le choix du visiteur, partagez-le avec chaque composant, activez ou bloquez les scripts lorsqu'il change, n'envoyez de pages vues lors des changements de route qu'avec un consentement, et faites en sorte que le retrait prenne effet immédiatement plutôt qu'au prochain chargement de page.

## Pourquoi le consentement aux cookies est-il plus complexe dans une application monopage ?

La plupart des mécanismes de consentement partent du principe que les pages se chargent entièrement, alors qu'une SPA se charge une fois puis remplace les vues en JavaScript. Quatre hypothèses tombent alors :

- **Les scripts ne se chargent qu'une fois.** Si la mesure d'audience a été bloquée au chargement et que le visiteur accepte plus tard, aucun chargement de page ne viendra l'activer. Vous devez l'activer sur place.
- **Les pages vues sont virtuelles.** Les changements de route ne rechargent pas la page : votre routeur doit donc signaler les pages vues, et vérifier le consentement au préalable.
- **Les choix changent en cours de session.** Un visiteur peut accepter, naviguer, puis retirer son consentement depuis un lien en pied de page alors que des scripts tournent déjà en mémoire.
- **Le rendu serveur ajoute l'hydratation.** Avec des frameworks comme Next.js ou Nuxt, le HTML du serveur doit correspondre au premier rendu côté client, mais le serveur ne connaît pas forcément le choix du visiteur.

## Comment modéliser l'état du consentement dans une SPA ?

Traitez le consentement comme un petit store que chaque partie de l'application lit, doté d'une méthode d'abonnement pour que les composants et les scripts réagissent aux changements. Réglez par défaut chaque catégorie non essentielle sur refusé, pour que le code exécuté avant le chargement du choix enregistré ne puisse jamais laisser fuiter de données.

```ts
// consent-store.ts
export type Category = 'necessary' | 'preferences' | 'analytics' | 'marketing';
export type Consent = Record<Category, boolean>;

export const DENIED: Consent = {
  necessary: true, preferences: false, analytics: false, marketing: false,
};

let state: Consent = DENIED;
const listeners = new Set<() => void>();

export const consentStore = {
  get: (): Consent => state,
  set(next: Partial<Consent>) {
    state = { ...state, ...next, necessary: true };
    listeners.forEach((notify) => notify());
  },
  subscribe(listener: () => void) {
    listeners.add(listener);
    return () => { listeners.delete(listener); };
  },
};
```

Dans une application réelle, `set` enregistrerait aussi le choix dans un cookie propriétaire (first-party), le consignerait dans votre journal de consentement et mettrait à jour Google Consent Mode avec `gtag('consent', 'update', …)`. Dans React, `useSyncExternalStore` relie le store aux composants, et le même hook peut conditionner les contenus intégrés qui déposent des cookies :

```tsx
// consent.tsx
import { useSyncExternalStore } from 'react';
import { consentStore, DENIED } from './consent-store';

export function useConsent() {
  return useSyncExternalStore(consentStore.subscribe, consentStore.get, () => DENIED);
}

export function VideoEmbed({ videoId }: { videoId: string }) {
  const { marketing } = useConsent();

  if (!marketing) {
    return (
      <div className="embed-placeholder">
        <p>Cette vidéo est hébergée par YouTube, qui peut déposer des cookies marketing.</p>
        <button type="button" onClick={() => consentStore.set({ marketing: true })}>
          Autoriser les cookies marketing et lire la vidéo
        </button>
      </div>
    );
  }

  return (
    <iframe
      src={`https://www.youtube-nocookie.com/embed/${videoId}`}
      title="Vidéo de présentation du produit"
      allowFullScreen
    />
  );
}
```

Le troisième argument de `useSyncExternalStore` est l'instantané utilisé pendant le rendu serveur et l'hydratation. Renvoyer `DENIED` à cet endroit garantit que le serveur et le premier rendu client concordent, et React refait le rendu avec l'état réel juste après l'hydratation. Dans Vue, un composable encapsulant une `ref` réactive joue le même rôle.

Pour les scripts qui ne sont pas des composants, abonnez-vous directement au store et chargez-les avec un `import()` dynamique une fois leur catégorie autorisée. Notre guide pour [bloquer les scripts jusqu'au consentement](/fr/blog/block-scripts-until-consent) présente les modes d'activation.

## Comment suivre les changements de route uniquement avec consentement ?

Envoyez une page vue virtuelle depuis le routeur après chaque navigation, et vérifiez le consentement à ce moment-là. Si le visiteur accepte la mesure d'audience en cours de session, il est raisonnable d'enregistrer la page sur laquelle il se trouve, puisque c'est là que commence son opt-in.

```tsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useConsent } from './consent';
import { trackPageView } from './analytics';

export function PageViewTracker() {
  const { pathname } = useLocation();
  const { analytics } = useConsent();

  useEffect(() => {
    if (analytics) trackPageView(pathname);
  }, [pathname, analytics]);

  return null;
}
```

Attention au double comptage. Certains outils de mesure d'audience peuvent envoyer automatiquement des pages vues lors des changements de l'historique du navigateur ; Google Analytics 4 le propose comme option de mesure améliorée. Utilisez soit l'option automatique, soit votre propre tracker, mais pas les deux.

## Comment éviter les incohérences d'hydratation et le clignotement du bandeau ?

Déterminez où le choix du visiteur est connu lors du premier rendu. Deux options solides s'offrent à vous :

- **Lire le cookie de consentement sur le serveur.** Les cookies sont envoyés avec chaque requête : les pages rendues côté serveur peuvent donc afficher ou omettre correctement le bandeau dès le premier octet. Cela ne fonctionne que si le HTML n'est pas mis en cache et partagé entre visiteurs.
- **Afficher le bandeau uniquement côté client.** Pour les pages générées statiquement ou mises en cache sur un CDN, n'affichez rien côté serveur et montrez le bandeau une fois l'application montée. Positionnez-le en superposition fixe pour qu'une apparition tardive ne provoque pas de décalage de mise en page ; voir [bandeaux cookies et Core Web Vitals](/fr/blog/cookie-banners-core-web-vitals).

Dans les deux cas, le cookie ou l'entrée de stockage qui mémorise le choix est généralement considéré comme nécessaire pour respecter ce choix ; voir [ce qui relève d'un cookie strictement nécessaire](/fr/blog/strictly-necessary-cookies).

## Comment gérer le retrait du consentement en cours de session ?

Rendez le retrait immédiat et aussi simple que le fait de donner son consentement, comme l'exige l'article 7(3) du RGPD. Concrètement, cela suppose un lien permanent « Paramètres des cookies », généralement en pied de page, qui rouvre les préférences sans chargement de page. Lorsqu'un visiteur retire son consentement pour une catégorie :

1. Mettez à jour le store et enregistrez le nouveau choix.
2. Si vous utilisez des balises Google, envoyez `gtag('consent', 'update', …)` avec les signaux concernés réglés sur `'denied'`.
3. Cessez d'appeler les fonctions de suivi ; le code conditionné par le store le fait automatiquement.
4. Supprimez les cookies propriétaires déposés par la catégorie retirée, lorsque vous connaissez leurs noms et domaines. Vos scripts ne peuvent pas supprimer les cookies déposés sur d'autres domaines.
5. Rechargez la page si un script retiré est déjà en cours d'exécution. Les bibliothèques conservent des minuteurs, des écouteurs et des identifiants en mémoire, et un rechargement est le seul moyen fiable de les effacer.

Un rechargement après un retrait est un compromis raisonnable : il est rare, et l'alternative consiste à exécuter du code que vous n'avez plus l'autorisation d'exécuter.

## En résumé

Dans une SPA, le consentement est un état : refusez par défaut, partagez un store unique, conditionnez-y les scripts et les contenus intégrés, n'envoyez de pages vues qu'avec un consentement et faites prendre effet le retrait immédiatement. Des outils comme Cookiesh peuvent prendre en charge une bonne partie de cette plomberie, avec un SDK React qui fournit `CookieshProvider` et un hook `useConsent()`, ainsi qu'un événement `consent:updated` pour tout le reste.

*Cet article est fourni à titre d'information générale et ne constitue pas un avis juridique. Pour toute décision concernant votre situation, consultez un professionnel qualifié de la protection des données.*

## FAQ

### Faut-il réafficher le bandeau cookies après chaque changement de route ?

Non. Affichez-le jusqu'à ce que le visiteur fasse un choix, enregistrez ce choix et conservez un lien permanent vers les paramètres. Ne redemandez que si vos finalités ou vos fournisseurs changent de façon significative, ou si le choix enregistré expire.

### Puis-je stocker le choix de consentement dans le localStorage plutôt que dans un cookie ?

Les deux fonctionnent dans le navigateur, mais un cookie est aussi envoyé à votre serveur avec chaque requête : les pages rendues côté serveur peuvent donc afficher le bon état dès le premier rendu. Le localStorage n'est lisible que par le code côté client, une fois l'application chargée.

### Les composants de script des frameworks, comme next/script, gèrent-ils le consentement à ma place ?

Non. Des utilitaires comme le composant Script de Next.js contrôlent le moment où un script se charge par rapport à l'hydratation, pas le fait que le visiteur l'ait accepté ou non. Affichez-les de manière conditionnelle en fonction de votre état de consentement, afin qu'ils ne soient pas ajoutés à la page avant un opt-in.

## Sources

- [Directive 2002/58/EC (ePrivacy Directive), EUR-Lex](https://eur-lex.europa.eu/eli/dir/2002/58/oj)
- [Regulation (EU) 2016/679 (GDPR), EUR-Lex](https://eur-lex.europa.eu/eli/reg/2016/679/oj)
- [Google : présentation du mode Consentement](https://developers.google.com/tag-platform/security/guides/consent)
