TL;DR
Od marca 2024 Google Consent Mode v2 jest wymagany dla ruchu z EOG/UK, jeśli używasz Google Ads lub GA4 z remarketingiem — inaczej tracisz modelowanie konwersji i część sygnałów do Smart Bidding. W Next.js wdrożenie to trzy warstwy: domyślny stan „denied” w gtag('consent', 'default', …) przed załadowaniem tagów, baner zapisujący wybór użytkownika (CookieBanner.tsx + localStorage), oraz gtag('consent', 'update', …) po akceptacji kategorii analitycznych/marketingowych. Na stronie DevStudio.it GA4 (G-3HT7CZTN7P) i trzy kontenery Ads (AW-17557280025, AW-17769880693, AW-18151506857) ładują się w layout.tsx — baner w [locale]/layout.tsx zapisuje preferencje, ale musi być połączony z Consent Mode, żeby wybór „Tylko niezbędne” realnie blokował sygnały reklamowe. Poniżej: implementacja, RODO, testy i typowe błędy.
Dla kogo to jest
- Właścicieli stron firmowych z Google Ads i GA4 w UE/UK
- Developerów Next.js integrujących CookieBanner z
gtag - Osób odpowiedzialnych za RODO/GDPR i marketing performance jednocześnie
- Zespołów widzących spadek danych konwersji po wdrożeniu banera cookies
Fraza (SEO)
consent mode v2 google, consent mode ga4 nextjs, baner cookies rodo google ads, default denied consent mode, testowanie consent mode tag assistant
Czym jest Consent Mode v2
Consent Mode to mechanizm Google, który przekazuje tagom (gtag, GTM) status zgody użytkownika na przechowywanie i odczyt cookies oraz wysyłanie danych do Google. Wersja v2 (obowiązkowa od 2024 dla Ads w EOG) rozszerza model o sygnały:
| Parametr | Znaczenie |
|---|---|
analytics_storage |
Cookies analityczne (GA4) |
ad_storage |
Cookies reklamowe |
ad_user_data |
v2 — przesyłanie danych użytkownika do reklam Google |
ad_personalization |
v2 — personalizacja reklam (remarketing) |
Bez v2 Google może ograniczyć remarketing, konwersje modelowane i zgodność konta Ads z wymogami UE — nawet jeśli tagi technicznie „są na stronie”.
Consent Mode nie zastępuje banera cookies — to warstwa techniczna po decyzji użytkownika (lub przed nią w trybie default denied).
Architektura na stronie Next.js — stan obecny
W projekcie DevStudio.it:
Tagi w src/app/layout.tsx
- GA4
G-3HT7CZTN7P— strategiaafterInteractive(ważne dlagenerate_leadprzy szybkim submit formularza) - Google Ads — trzy kontenery
AW-ze strategiąlazyOnload(LCP) - reCAPTCHA v3 — lazyOnload
GA4 config zawiera m.in. anonymize_ip: true i allow_ad_personalization_signals: false — to dobry punkt wyjścia pod RODO, ale nie zastępuje Consent Mode.
Baner w src/components/CookieBanner.tsx
Komponent kliencki ('use client') renderowany w src/app/[locale]/layout.tsx:
- Po 2 s sprawdza
localStorage.getItem('cookieConsent') - Zaakceptuj wszystkie →
analytics: true,marketing: true - Tylko niezbędne →
analytics: false,marketing: false - Preferencje w
cookiePreferencesjako JSON
To poprawny wzorzec UX (opóźnienie banera, trzy przyciski, link do polityki prywatności). Brakujący element produkcyjny: wywołanie gtag('consent', 'update', …) zsynchronizowane z przyciskami — bez tego localStorage i tagi Google żyją osobno.
Default denied vs granted — co wybrać
Default denied (zalecane w EOG)
Przed interakcją użytkownika wszystkie sygnały poza niezbędnymi są odmówione:
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500,
});Tagi mogą się załadować, ale wysyłają pingi bez cookies — Google modeluje część konwersji statystycznie. To zgodne z RODO, gdy baner informuje o cookies i daje realny wybór.
Default granted (ryzykowne w UE)
Wszystko „tak” do momentu kliknięcia „Odrzuć” — praktycznie nie spełnia standardu opt-in w większości interpretacji UODO/EDPB. Stosuj tylko poza EOG lub gdy prawnik wyraźnie zatwierdzi inną podstawę prawną.
wait_for_update
Parametr wait_for_update: 500 (ms) daje banerowi czas na ustawienie consent update przed pierwszym pełnym hitem — redukuje „flash” pełnego trackingu przed decyzją.
Implementacja krok po kroku w Next.js 15
Krok 1: Consent default przed config GA4/Ads
W layout.tsx, przed gtag('config', 'G-...'):
<Script id="google-consent-default" strategy="beforeInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500,
region: ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','IS','LI','NO','GB']
});
`}
</Script>Tablica region ogranicza strict default do EOG+UK; poza tym regionem możesz ustawić granted (Google obsługuje to w jednym wywołaniu z mapą regionów — patrz dokumentacja gtag).
Krok 2: update w CookieBanner
Po handleAcceptAll:
const grantAll = () => {
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('consent', 'update', {
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
});
}
localStorage.setItem('cookieConsent', 'accepted');
localStorage.setItem('cookiePreferences', JSON.stringify({
necessary: true, analytics: true, marketing: true,
}));
setIsVisible(false);
};Po handleAcceptNecessary — update denied (lub tylko analytics_storage: 'denied' jeśli chcesz modelowanie Ads):
window.gtag?.('consent', 'update', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
});Krok 3: Przywrócenie zgody po reload
W useEffect CookieBanner — jeśli cookieConsent === 'accepted', odczytaj cookiePreferences i wywołaj consent update zanim user zobaczy baner ponownie:
useEffect(() => {
const consent = localStorage.getItem('cookieConsent');
const prefsRaw = localStorage.getItem('cookiePreferences');
if (consent && prefsRaw) {
const prefs = JSON.parse(prefsRaw);
window.gtag?.('consent', 'update', {
analytics_storage: prefs.analytics ? 'granted' : 'denied',
ad_storage: prefs.marketing ? 'granted' : 'denied',
ad_user_data: prefs.marketing ? 'granted' : 'denied',
ad_personalization: prefs.marketing ? 'granted' : 'denied',
});
return;
}
const timer = setTimeout(() => setIsVisible(true), 2000);
return () => clearTimeout(timer);
}, []);Krok 4: Lazy load Ads dopiero po marketing granted (opcjonalnie, agresywne RODO)
Alternatywa: nie ładuj skryptów AW- w layout wcale — inject dynamicznie po marketing: true. Maksymalna zgodność, ale utrudnia modelowanie i wymaga refaktoru layout.tsx. Consent Mode z default denied + loaded tags to kompromis akceptowany przez większość implementacji przy poprawnym banerze.
GA4 G-3HT7CZTN7P — co się zmienia po Consent Mode
Przy analytics_storage: 'denied':
- GA4 wysyła cookieless pings (consent mode modeling)
- Raporty Realtime mogą pokazywać mniej sesji do czasu zgody
- Zdarzenia
generate_leadpo submit formularza powinny trafiać po granted — testuj formularz po „Zaakceptuj wszystkie”
Przy afterInteractive GA4 ładuje się wcześnie — dlatego default denied musi być beforeInteractive, inaczej pierwszy page_view może wylecieć z pełnymi cookies.
Stała GA4_MEASUREMENT_ID w src/lib/ga4-measurement-id.ts musi pozostać zsynchronizowana z layout.tsx.
Google Ads AW- — trzy kontenery i konwersje
Projekt używa trzech tagów Ads (lazyOnload). Consent Mode dotyczy wszystkich — wspólny dataLayer i jedno consent update aktualizuje zachowanie każdego gtag('config', 'AW-...').
Po „Tylko niezbędne”:
- Konwersje
conversion_event_submit_lead_formmogą być modelowane, nie mierzone 1:1 - Smart Bidding nadal dostaje sygnały aggregate, ale z opóźnieniem i niższą granularnością
- Tag Assistant pokaże status consent per hit
Po „Zaakceptuj wszystkie” — pełne cookies _gcl_*, pełniejsza atrybucja.
Ważne: test konwersji Ads w incognito bez akceptacji marketingowej da inny wynik niż test po akceptacji — to nie bug, to RODO.
RODO / GDPR — baner vs Consent Mode
| Wymóg prawny (uproszczenie) | Baner CookieBanner | Consent Mode |
|---|---|---|
| Informacja przed cookies | Tekst + link do polityki | Nie wystarczy sam |
| Wybór granularny | „Dostosuj” z kategoriami | Mapowanie kategorii → parametry gtag |
| Opt-in na marketing | Przycisk „Tylko niezbędne” jako default action | default denied |
| Dowód zgody | localStorage + opcjonalnie log serwerowy | Logi Google (suplement) |
localStorage nie jest cookie — nadal dokumentuj w polityce prywatności, że preferencje są zapisywane lokalnie. Dla audytu rozważ endpoint zapisujący hash decyzji + timestamp (bez PII).
Kategorie w banerze:
- Niezbędne — zawsze on (sesja, CSRF, reCAPTCHA może być „niezbędne” jeśli chroni formularz — skonsultuj z prawnikiem)
- Analityczne →
analytics_storage - Marketingowe →
ad_storage,ad_user_data,ad_personalization
Przycisk „Dostosuj” w obecnym UI pokazuje kategorie, ale toggle nie są interaktywne — to kolejny krok rozwoju: prawdziwe switch'e zapisujące częściową zgodę (analytics tak, marketing nie).
Testowanie Consent Mode v2
Tag Assistant Companion
- Otwórz stronę produkcyjną w incognito.
- Bez akceptacji cookies — wyślij formularz testowy.
- W Tag Assistant sprawdź Consent State — powinno być
denieddla ad/analytics. - Zaakceptuj wszystkie — odśwież — powtórz submit.
- Porównaj: GA4 Realtime, Ads konwersje (24–48 h opóźnienia).
Chrome DevTools → Application
- Cookies — po denied brak
_ga,_gcl_au(do czasu granted) - Local Storage —
cookieConsent,cookiePreferences
Google Ads — diagnostyka
W Cele → Ustawienia → Consent mode status powinien być „Wykryto” po wdrożeniu default + update. „Nie wykryto” = tagi ignorują sygnały lub default jest za późno.
Checklist przed audytem RODO
-
consent defaultbeforeInteractive, przed GA4 config -
consent updatena każdy przycisk banera - Przywrócenie zgody z localStorage po reload
- Formularz + chatbot testowane w obu ścieżkach zgody
- Polityka prywatności opisuje kategorie i Google jako procesor
- Brak „ciemnych wzorców” (pre-checked marketing)
Wpływ na Core Web Vitals
beforeInteractive consent script to mały inline — minimalny wpływ na LCP. GA4 afterInteractive + Ads lazyOnload pozostaje dobrym układem po dodaniu Consent Mode — nie przenoś wszystkiego na beforeInteractive „dla pełnych danych”.
Modelowane konwersje przy denied nie wymagają pełnego ładowania wszystkich AW- przed zgodą — Google explicite projektuje ten trade-off.
Spójność: formularz, chatbot i wielojęzyczność
Lead może trafić z formularza kontaktowego (page.tsx → /api/submissions) albo z chatbota (Chatbot.tsx → /api/chatbot/submit). Oba wysyłają generate_lead i conversion_event_submit_lead_form po HTTP 200. Test Consent Mode musi obejmować obie ścieżki — inaczej marketing widzi „dziury” w konwersjach mimo poprawnego banera na stronie głównej.
Wersje /pl, /en, /de dzielą ten sam layout.tsx z tagami i ten sam CookieBanner w locale layout. Teksty banera są dziś po polsku niezależnie od locale — kolejny krok i18n to tłumaczenia przycisków i mapowanie linku polityki (/${locale}/polityka). Consent Mode nie zależy od języka — parametry granted/denied są uniwersalne.
Harmonogram wdrożenia (1–2 dni dev)
| Dzień | Zadanie | Weryfikacja |
|---|---|---|
| 1 rano | consent default beforeInteractive w layout |
Tag Assistant: denied przed kliknięciem |
| 1 popołudnie | consent update w CookieBanner + restore z localStorage |
Reload strony zachowuje wybór |
| 2 rano | Test formularza + chatbota (denied / granted) | GA4 Realtime, brak _ga przy denied |
| 2 popołudnie | Ads diagnostyka + aktualizacja polityki prywatności | Panel Ads: Consent mode „Wykryto” |
Po wdrożeniu odczekaj 48 h przed oceną kampanii Ads — modelowanie konwersji stabilizuje się dopiero po zebraniu próby ruchu w obu stanach zgody.
FAQ
Czy muszę używać GTM zamiast gtag.js?
Nie. Consent Mode działa z bezpośrednim gtag w layout.tsx tak samo jak z GTM. GTM ułatwia zarządzanie wieloma tagami bez deployu kodu — ale Next.js Script + stałe ID to valid pattern.
Czy „Tylko niezbędne” blokuje GA4 całkowicie?
Przy poprawnym Consent Mode GA4 nadal wysyła ograniczone pingi (modelowanie), ale bez cookies analitycznych. Raporty będą mniej kompletne do momentu granted. To oczekiwane.
Czy reCAPTCHA wymaga zgody marketingowej?
reCAPTCHA Google to osobna usługa — często klasyfikowana jako „niezbędna” do bezpieczeństwa formularza, ale przesyła dane do Google. UODO może wymagać wzmianki w polityce. Consent Mode nie kontroluje iframe reCAPTCHA — rozważ ładowanie skryptu dopiero przy focus na formularzu.
Co jeśli użytkownik wyczyści localStorage?
Baner pojawi się ponownie; default denied zadziała do następnej decyzji. To poprawne zachowanie.
Czy Consent Mode v2 wystarczy dla Google Ads w Polsce?
Technicznie tak — przy default denied, update po zgodzie i banerze spełniającym RODO. Prawnie potrzebujesz też polityki prywatności, ewentualnie DPA z Google i dokumentacji procesów.
Dlaczego mam trzy tagi AW-?
Migracja kont reklamowych / kampanii historycznych. Consent Mode aktualizuje wspólny dataLayer — nie musisz trzy razy wywoływać update, wystarczy jedno gtag('consent', 'update', …).
Podsumowanie
Consent Mode v2 to most między RODO a skutecznością Google Ads/GA4: default denied w EOG, baner z realnym wyborem (jak CookieBanner.tsx), gtag consent update zsynchronizowany z localStorage, testy w Tag Assistant w obu ścieżkach zgody. Sam baner bez Consent Mode to połowa wdrożenia; same tagi bez banera to ryzyko prawne. Na Next.js 15 ustaw default beforeInteractive, zachowaj GA4 afterInteractive dla leadów i Ads lazyOnload dla wydajności — potem połącz przyciski banera z analytics_storage i parametrami ad_ v2.
Chcesz wdrożyć Consent Mode u siebie?
- Skontaktuj się — połączymy baner, GA4 i Google Ads zgodnie z RODO
- Polityka prywatności i cookies — wzór dokumentacji na stronie
- Strony WWW — Next.js, analityka i zgodność prawna w jednym projekcie