Google Consent Mode v2 en Next.js: implementación completa
Desde marzo de 2024 es obligatorio implementar Google Consent Mode v2 para que Google Ads y GA4 sigan funcionando correctamente con usuarios del EEE. La mayoría de tutoriales se quedan en el script del banner y olvidan cómo integrarlo bien con Next.js App Router, qué estrategia de carga usar y cómo sincronizar los updates de consentimiento. Esta guía cubre la implementación completa con código real extraído de producción.
Qué es Consent Mode v2 y por qué es obligatorio
Consent Mode es la API de Google que gobierna qué pueden hacer los tags de analítica y publicidad según el consentimiento del usuario. La versión 2, activa desde marzo de 2024, añadió dos parámetros respecto de v1:
ad_user_data— si se permite enviar datos personales a Google para fines publicitarios.ad_personalization— si se permite usarlos para personalizar anuncios.
Sin esos dos parámetros correctamente gestionados, Google Ads deja de poder construir audiencias de remarketing y la medición de conversiones se deteriora. No es una obligación legal formal — la obligación legal viene del RGPD — pero a efectos prácticos, sin Consent Mode v2 correctamente implementado, tu stack publicitario se rompe.
Modos Basic vs Advanced
| Aspecto | Basic | Advanced |
|---|---|---|
| Scripts antes de consentir | No se cargan | Sí se cargan (bloqueados por consent) |
| Datos al rechazar | Ninguno | Pings cookieless anonimizados |
| Modelado de conversiones | No disponible | Sí (mejor fidelidad) |
| Complejidad técnica | Baja | Media |
| Recomendado para | Webs con pocos usuarios o sin Ads | Webs que usan Google Ads activamente |
Esta guía implementa Advanced Mode, que es el estándar en 2026 para cualquier web con publicidad. Si solo usas GA4 sin Ads, puedes optar por Basic con un ajuste mínimo.
Implementación paso a paso en Next.js
Paso 1 — Registrar el consent default en layout.tsx
Usamos next/script con strategy="beforeInteractive" para garantizar que el default se ejecuta antes que cualquier otra cosa:
import Script from "next/script";
const GA_MEASUREMENT_ID = "G-XXXXXXXXXX";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es-ES">
<head>
{/* Consent Mode v2 — debe cargar ANTES que cualquier script de Google */}
<Script id="consent-mode" strategy="beforeInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
window.gtag = gtag;
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
functionality_storage: 'granted',
security_storage: 'granted',
wait_for_update: 500
});
gtag('set', 'ads_data_redaction', true);
`}
</Script>
{/* GA4 — se carga pero solo envía eventos tras consentimiento */}
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
strategy="afterInteractive"
/>
<Script id="ga4-init" strategy="afterInteractive">
{`
gtag('js', new Date());
gtag('config', '${GA_MEASUREMENT_ID}', { anonymize_ip: true });
`}
</Script>
</head>
<body>{children}</body>
</html>
);
}Tres detalles clave:
wait_for_update: 500le dice a Google que espere 500ms por si el usuario acepta antes.ads_data_redactiongarantiza anonimización extra mientras el consent esté denegado.functionality_storageysecurity_storagese otorgan por defecto — son cookies técnicas imprescindibles, no requieren consentimiento según RGPD.
Paso 2 — Crear el banner de cookies
Componente cliente con dos opciones básicas (Aceptar / Rechazar) y una tercera avanzada opcional:
"use client";
import { useEffect, useState } from "react";
declare global {
interface Window {
gtag: (...args: unknown[]) => void;
}
}
const STORAGE_KEY = "tm-consent-v2";
type Consent = "granted" | "denied";
interface ConsentState {
ad_storage: Consent;
ad_user_data: Consent;
ad_personalization: Consent;
analytics_storage: Consent;
}
export default function CookieBanner() {
const [visible, setVisible] = useState(false);
useEffect(() => {
const saved = localStorage.getItem(STORAGE_KEY);
if (!saved) {
setVisible(true);
} else {
try {
const parsed: ConsentState = JSON.parse(saved);
window.gtag?.("consent", "update", parsed);
} catch {
localStorage.removeItem(STORAGE_KEY);
setVisible(true);
}
}
}, []);
const persistAndUpdate = (state: ConsentState) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
window.gtag?.("consent", "update", state);
setVisible(false);
};
const acceptAll = () => persistAndUpdate({
ad_storage: "granted",
ad_user_data: "granted",
ad_personalization: "granted",
analytics_storage: "granted",
});
const rejectAll = () => persistAndUpdate({
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
analytics_storage: "denied",
});
if (!visible) return null;
return (
<div className="fixed bottom-4 left-4 right-4 md:max-w-md z-[9999] p-6 rounded-2xl bg-neutral-900 border border-white/10">
<h2 className="text-lg font-bold mb-2">Cookies</h2>
<p className="text-sm text-white/60 mb-4">
Usamos cookies propias y de terceros para analítica y publicidad.
Puedes aceptar todas o rechazar las no esenciales.
</p>
<div className="flex gap-2">
<button onClick={rejectAll} className="px-4 py-2 text-sm border border-white/15 rounded">
Rechazar
</button>
<button onClick={acceptAll} className="px-4 py-2 text-sm bg-white text-black rounded">
Aceptar
</button>
</div>
</div>
);
}Paso 3 — Montar el banner en el layout
import CookieBanner from "@/components/CookieBanner";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es-ES">
<head>{/* scripts de Consent Mode + GA4 */}</head>
<body>
{children}
<CookieBanner />
</body>
</html>
);
}Paso 4 — Añadir página de política de cookies
RGPD exige información clara antes de pedir consentimiento. Crea /cookies con tabla de cookies usadas (propias y de terceros), finalidad y duración. Enlaza desde el banner.
Paso 5 — Permitir re-gestionar el consentimiento
RGPD requiere que el usuario pueda retirar su consentimiento tan fácilmente como lo otorgó. Añade un botón "Gestionar cookies" en el footer que limpie el localStorage y vuelva a mostrar el banner.
Cómo verificar que funciona
1. Tag Assistant de Google
Instala Google Tag Assistant y navega por tu web. Debe mostrar "Consent mode signal: detected" con los valores correctos antes y después de aceptar.
2. DevTools → Application → Cookies
Con consentimiento denegado, no deben aparecer cookies con prefijo _ga, _gid, _fbp. Tras aceptar, sí deben aparecer.
3. Network → gtag/js?
Antes de aceptar, las llamadas a google-analytics.com/g/collect deben llevar _p=1 y otros flags de cookieless. Tras aceptar, pasan a llamadas normales con identificadores.
4. Search Console → Consent Mode
En GA4 → Configuración → Consent mode settings, verifica que el estado es "Detected" y que hay volumen de pings tanto granted como denied.
Errores comunes
"El consent default no se registra antes de GA"
Casi siempre por usar strategy="afterInteractive" en el script de consent mode. Debe ser beforeInteractive. Si usas Pages Router en lugar de App Router, colócalo en _document.tsx como raw <script>.
"El banner parpadea al cargar"
El componente es "use client" y monta tras hydration. Mientras, el estado visible está en false y luego cambia. Soluciona con suppressHydrationWarning o renderizando el banner desde el servidor con una cookie HTTP-only que indique si ya fue visto.
"Google Ads sigue sin recibir datos"
Revisa que ad_user_data y ad_personalization pasen a granted cuando el usuario acepta. En v2 son obligatorios; si solo actualizas ad_storage, Ads no habilita remarketing.
Checklist final GDPR + Consent Mode v2
- ☐ Consent default registrado antes de cualquier script de Google
- ☐ Banner visible en primera visita
- ☐ Opciones claras: Aceptar / Rechazar / (opcional) Configurar
- ☐ Los 4 parámetros de v2 actualizados correctamente
- ☐ Consentimiento persistido entre sesiones
- ☐ Página
/cookiesaccesible desde el banner - ☐ Botón "Gestionar cookies" en el footer
- ☐ Verificación con Tag Assistant y DevTools
- ☐ Texto del banner en el idioma principal del sitio
- ☐ Política de privacidad actualizada con mención a Consent Mode
Conclusión
Implementar Consent Mode v2 bien en Next.js no es complicado si se cumplen dos cosas: registrar el default antes de cualquier otro script y gestionar los cuatro parámetros completos cuando el usuario responde. El código de esta guía es el mismo que tenemos en producción en thinkmadrid.com, lleva meses sin tocar y pasa auditorías de Tag Assistant sin alertas.
Si quieres seguir por el lado técnico, lee también la guía de Next.js 16. Y si necesitas que revisemos tu implementación, escríbenos a /contacto.
Preguntas frecuentes
¿Qué es Google Consent Mode v2?
Consent Mode v2 es la segunda versión del sistema de Google para recoger el consentimiento del usuario antes de activar cookies de analítica y publicidad. Introdujo dos parámetros nuevos (ad_user_data y ad_personalization) que son obligatorios en el Espacio Económico Europeo desde marzo de 2024, y sin los cuales Google Ads deja de recibir datos para audiencias y remarketing.
¿Es obligatorio implementar Consent Mode v2?
Es obligatorio para cualquier web que use Google Analytics 4, Google Ads o Floodlight y reciba usuarios del EEE o Reino Unido. Sin Consent Mode v2, Google Ads dejará de procesar datos de conversión y remarketing. No es oficialmente una obligación legal, pero sin él tu stack publicitario deja de funcionar a efectos prácticos.
¿Cuál es la diferencia entre Consent Mode v1 y v2?
V2 añade dos parámetros nuevos: ad_user_data (si puedes enviar datos personales a Google para publicidad) y ad_personalization (si puedes usar esos datos para personalizar anuncios). V1 solo tenía ad_storage y analytics_storage. V2 también formaliza dos modos: Basic (no cargar scripts hasta aceptar) y Advanced (cargar pero enviar señales anonimizadas).
¿Qué pasa si el usuario no acepta cookies?
En modo Advanced, Google recibe pings cookieless (sin identificadores personales) que le permiten estimar conversiones mediante modelado. En modo Basic, no se envía nada. Con Advanced pierdes granularidad pero conservas métricas aproximadas; con Basic cumples estrictamente pero pierdes visibilidad total de usuarios que rechazan.
¿Dónde debo cargar el script de Consent Mode en Next.js?
En layout.tsx, dentro del <head> y con estrategia beforeInteractive, antes del script de Google Analytics. Es crítico que el consent default se registre antes de cualquier llamada a gtag, para que GA no envíe datos identificables sin permiso. Si usas App Router, usa el componente Script de next/script con strategy='beforeInteractive'.
¿Cómo actualizo el consentimiento cuando el usuario acepta?
Llamando a gtag('consent', 'update', { ... }) con los valores nuevos. Es importante persistir la elección en localStorage o en una cookie first-party para que futuras visitas no vuelvan a mostrar el banner. El evento 'update' debe disparar también antes de cargar cualquier Tag Manager adicional.
¿Tienes un proyecto en mente?
Cuéntanos qué necesitas y te proponemos la mejor solución sin compromiso.
Hablar con el equipo →