Consent Mode v2
Consent Mode v2 is Google’s framework for transmitting granular advertising consent state. There are four signals; each is 'granted' or 'denied'. Trackbridge stores all four and acts on one — ad_storage. The others are recorded so that future SDK versions and downstream consumers can read a consistent state.
The four signals
Section titled “The four signals”| Signal | What it covers | Trackbridge behavior |
|---|---|---|
ad_storage | Cookies and storage related to advertising | Gates the _tb_* first-party cookies. Pre-grant click identifiers held in memory; persisted on grant. |
ad_user_data | Sending user data (email, phone, address) to Google for advertising | Stored only. Currently does not gate userData transmission — that’s your call to make in application code. |
ad_personalization | Personalized ads (remarketing, audience targeting) | Stored only. |
analytics_storage | Cookies and storage related to analytics (GA4) | Stored only. The SDK does not write GA4 cookies; gtag does that and respects this signal independently. |
The signals you don’t act on are still useful: they’re available to gtag, to your own logging, to a future SDK version that adds enforcement.
What ad_storage gates
Section titled “What ad_storage gates”Specifically: writing _tb_gclid, _tb_gbraid, and _tb_wbraid. Nothing else.
When ad_storage === 'granted':
- New click identifiers captured from the URL are written to cookies immediately.
- Identifiers held in memory from before consent are persisted.
- Existing cookies are read on subsequent page loads to re-hydrate state.
When ad_storage === 'denied' (or unknown):
- No new cookies are written.
- Click identifiers captured from the URL stay in memory only — useful for the current tab, gone on close.
- Existing cookies (set during a prior consent state) are not deleted by the SDK. Clearing them is up to you.
The deferred-persistence pattern
Section titled “The deferred-persistence pattern”The SDK is built around the assumption that consent might not be known when the page loads. The user lands on ?gclid=Cj0KCQ..., the CMP banner is loading, the user hasn’t clicked anything yet. Three states matter:
- Init time.
ad_storageis unknown (treated as not-granted). Thegclidis captured into the tracker’s memory but not written to a cookie. - Consent grant. Your CMP fires its onAccept callback, you call
tracker.updateConsent({ ad_storage: 'granted', ... }). The in-memorygclidis now persisted to_tb_gclid. - Consent deny. The user clicks decline.
tracker.updateConsent({ ad_storage: 'denied', ... })is recorded. The in-memorygclidstays for the lifetime of the tab. No cookie is written.
This is the right behavior under GDPR: identifiers can be used in the current session if the user is mid-flow, but not persisted across sessions without consent.
What you have to wire
Section titled “What you have to wire”import { tracker } from '@/lib/tracker.client';
// In your CMP's grant callback:tracker.updateConsent({ ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted', analytics_storage: 'granted',});
// In your CMP's deny callback:tracker.updateConsent({ ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied',});For per-CMP wiring (Cookiebot, OneTrust, Iubenda, custom), see Wiring with a CMP.
Replay on every page load. Don’t make the user re-consent every visit. If your CMP stores its choice in a cookie, read it as soon as the tracker is initialized and call updateConsent immediately:
const saved = readCmpCookie();if (saved) { tracker.updateConsent({ ad_storage: saved.marketing ? 'granted' : 'denied', /* ... */ });}Without the replay, every page load starts with consent unknown, and the SDK keeps captured identifiers in memory for the duration of that load — nothing terrible, but you lose the cookie’s cross-session continuity.
What this does NOT do
Section titled “What this does NOT do”- It does not show a consent banner. That’s your CMP’s job.
- It does not call
gtag('consent', ...)for you. Trackbridge tracks its own consent state separately. If you want gtag’s signals updated too (which you usually do for GA4 cookie behavior), callgtag('consent', 'update', { ... })from the same CMP callback. - It does not delete existing cookies on consent revocation. Either rotate cookie names per consent epoch yourself, or set short expiries, or accept that previously-set cookies live to their TTL.
- It does not server-side enforce anything. The server tracker assumes you’ve gated the call in application code. If you call
serverTracker.trackConversionfor a user who declined consent, it fires.
See also
Section titled “See also”tracker.updateConsent()- Wiring with a CMP — Cookiebot, OneTrust, Iubenda, custom.
- Google’s Consent Mode v2 reference.