# Gestire il consenso ai cookie nelle single-page app

> Come gestire il consenso ai cookie in React, Vue e altre SPA: stato condiviso, tracciamento dei cambi di route, hydration SSR, embed bloccati e revoca immediata.

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

## Key takeaways

- In una single-page app il consenso è stato applicativo: memorizzalo una sola volta, condividilo con ogni componente e reagisci alle modifiche senza ricaricare la pagina.
- Imposta come negata ogni categoria non essenziale per impostazione predefinita, così il codice eseguito prima del caricamento della scelta memorizzata non può far trapelare dati.
- Invia page view virtuali ai cambi di route solo se il visitatore ha accettato i cookie di analisi.
- Leggi il cookie di consenso durante il rendering lato server, oppure mostra il banner solo lato client, per evitare mismatch di hydration e lo sfarfallio del banner.
- Revocare il consenso deve essere facile quanto prestarlo, e ricaricare la pagina è spesso il modo più pulito per fermare gli script già in esecuzione.

In una single-page app (SPA) la pagina non si ricarica mai completamente, quindi il consenso ai cookie deve vivere nello stato dell'applicazione e non in uno script che viene eseguito una sola volta. Memorizza la scelta del visitatore, condividila con ogni componente, attiva o blocca gli script quando cambia, invia le page view legate ai cambi di route solo con il consenso e fai in modo che la revoca abbia effetto immediato, non al caricamento della pagina successiva.

## Perché il consenso ai cookie è più complesso in una single-page app?

La maggior parte dei modelli di consenso presuppone caricamenti completi della pagina, mentre una SPA si carica una volta sola e poi cambia le viste con JavaScript. Questo fa saltare quattro presupposti:

- **Gli script si caricano una volta sola.** Se l'analisi era bloccata al caricamento e il visitatore accetta in seguito, nessun caricamento di pagina la attiverà. Devi attivarla sul posto.
- **Le page view sono virtuali.** I cambi di route non ricaricano la pagina, quindi è il router a dover segnalare le page view, e deve prima verificare il consenso.
- **Le scelte cambiano durante la sessione.** Un visitatore può accettare, navigare e poi revocare il consenso da un link nel footer mentre gli script sono già in esecuzione in memoria.
- **Il rendering lato server aggiunge l'hydration.** Con framework come Next.js o Nuxt, l'HTML del server deve corrispondere al primo rendering lato client, ma il server potrebbe non conoscere la scelta del visitatore.

## Come devo modellare lo stato del consenso in una SPA?

Tratta il consenso come un piccolo store da cui legge ogni parte dell'app, con un metodo subscribe che permetta a componenti e script di reagire alle modifiche. Imposta come negata ogni categoria non essenziale per impostazione predefinita, così il codice eseguito prima del caricamento della scelta memorizzata non potrà mai far trapelare dati.

```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); };
  },
};
```

In un'app reale, `set` salverebbe anche la scelta in un cookie di prima parte, la registrerebbe nel log del consenso e aggiornerebbe Google Consent Mode con `gtag('consent', 'update', …)`. In React, `useSyncExternalStore` collega lo store ai componenti, e lo stesso hook può bloccare gli embed che impostano cookie:

```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>Questo video è ospitato su YouTube, che potrebbe impostare cookie di marketing.</p>
        <button type="button" onClick={() => consentStore.set({ marketing: true })}>
          Consenti i cookie di marketing e riproduci
        </button>
      </div>
    );
  }

  return (
    <iframe
      src={`https://www.youtube-nocookie.com/embed/${videoId}`}
      title="Video del prodotto"
      allowFullScreen
    />
  );
}
```

Il terzo argomento di `useSyncExternalStore` è lo snapshot usato durante il rendering lato server e l'hydration. Restituire `DENIED` in quel punto fa sì che il server e il primo rendering lato client coincidano, e React esegue di nuovo il rendering con lo stato reale subito dopo l'hydration. In Vue, un composable che incapsula un `ref` reattivo svolge lo stesso ruolo.

Per gli script che non sono componenti, iscriviti direttamente allo store e caricali con un `import()` dinamico quando la loro categoria viene autorizzata. La nostra guida su [come bloccare gli script fino al consenso](/it/blog/block-scripts-until-consent) illustra i modelli di attivazione.

## Come traccio i cambi di route solo con il consenso?

Invia una page view virtuale dal router dopo ogni navigazione e verifica il consenso in quel momento. Se il visitatore concede il consenso all'analisi durante la sessione, è ragionevole registrare la pagina in cui si trova, perché è lì che inizia l'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;
}
```

Attenzione al doppio conteggio. Alcuni strumenti di analisi possono inviare automaticamente le page view quando cambia la cronologia del browser; Google Analytics 4 lo offre come opzione della misurazione avanzata. Usa l'opzione automatica oppure il tuo tracker, non entrambi.

## Come evito i mismatch di hydration e lo sfarfallio del banner?

Decidi dove la scelta del visitatore è nota al primo rendering. Ci sono due opzioni valide:

- **Leggi il cookie di consenso sul server.** I cookie vengono inviati con ogni richiesta, quindi le pagine renderizzate lato server possono mostrare o nascondere correttamente il banner fin dal primo byte. Funziona solo se l'HTML non viene messo in cache e condiviso tra visitatori diversi.
- **Mostra il banner solo lato client.** Per le pagine generate staticamente o messe in cache da una CDN, non renderizzare nulla sul server e mostra il banner dopo il mount dell'app. Posizionalo come overlay fisso, così la comparsa ritardata non causa layout shift; leggi [banner dei cookie e Core Web Vitals](/it/blog/cookie-banners-core-web-vitals).

In entrambi i casi, il cookie o la voce di storage che ricorda la scelta è generalmente considerato necessario per rispettare quella scelta; leggi [che cosa si intende per cookie strettamente necessario](/it/blog/strictly-necessary-cookies).

## Come gestisco la revoca del consenso durante la sessione?

Rendi la revoca immediata e facile quanto prestare il consenso, come richiede l'articolo 7, paragrafo 3, del Regolamento generale sulla protezione dei dati (GDPR). In pratica significa un link “Impostazioni cookie” sempre disponibile, di solito nel footer, che riapre le preferenze senza ricaricare la pagina. Quando un visitatore revoca il consenso per una categoria:

1. Aggiorna lo store e salva la nuova scelta.
2. Se usi tag Google, invia `gtag('consent', 'update', …)` con i segnali pertinenti impostati su `'denied'`.
3. Smetti di chiamare le funzioni di tracciamento; il codice vincolato allo store lo fa automaticamente.
4. Elimina i cookie di prima parte impostati dalla categoria revocata, se ne conosci nomi e domini. I tuoi script non possono eliminare i cookie impostati su altri domini.
5. Ricarica la pagina se uno script revocato è già in esecuzione. Le librerie mantengono timer, listener e ID in memoria, e ricaricare è l'unico modo affidabile per eliminarli.

Ricaricare la pagina dopo una revoca è un compromesso accettabile: succede raramente, e l'alternativa è eseguire codice per cui non hai più l'autorizzazione.

## In sintesi

In una SPA il consenso è stato: parti da negato, condividi un unico store, usalo per bloccare script ed embed, invia le page view solo con il consenso e fai in modo che la revoca abbia effetto immediato. Strumenti come Cookiesh possono farsi carico di gran parte di questo lavoro di infrastruttura, con un SDK React che fornisce `CookieshProvider` e un hook `useConsent()`, oltre a un evento `consent:updated` per tutto il resto.

*Questo articolo ha carattere puramente informativo e non costituisce consulenza legale. Per decisioni relative alla tua situazione specifica, rivolgiti a un professionista qualificato in materia di privacy.*

## FAQ

### Devo mostrare di nuovo il banner dei cookie a ogni cambio di route?

No. Mostralo finché il visitatore non fa una scelta, memorizza quella scelta e mantieni un link sempre disponibile alle impostazioni. Chiedi di nuovo solo quando le finalità o i fornitori cambiano in modo sostanziale o quando la scelta memorizzata scade.

### Posso memorizzare la scelta di consenso in localStorage invece che in un cookie?

Nel browser funzionano entrambi, ma un cookie viene inviato anche al tuo server con ogni richiesta, così le pagine renderizzate lato server possono mostrare lo stato corretto fin dal primo rendering. localStorage è leggibile solo dal codice lato client dopo il caricamento dell'app.

### I componenti per gli script dei framework, come next/script, gestiscono il consenso al posto mio?

No. Strumenti come il componente Script di Next.js controllano quando uno script si carica rispetto all'hydration, non se il visitatore lo ha accettato. Renderizzali in modo condizionale in base allo stato del consenso, così non vengono aggiunti alla pagina prima di 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: Consent mode overview](https://developers.google.com/tag-platform/security/guides/consent)
