Skip to content

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.

SignalWhat it coversTrackbridge behavior
ad_storageCookies and storage related to advertisingGates the _tb_* first-party cookies. Pre-grant click identifiers held in memory; persisted on grant.
ad_user_dataSending user data (email, phone, address) to Google for advertisingStored only. Currently does not gate userData transmission — that’s your call to make in application code.
ad_personalizationPersonalized ads (remarketing, audience targeting)Stored only.
analytics_storageCookies 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.

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 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:

  1. Init time. ad_storage is unknown (treated as not-granted). The gclid is captured into the tracker’s memory but not written to a cookie.
  2. Consent grant. Your CMP fires its onAccept callback, you call tracker.updateConsent({ ad_storage: 'granted', ... }). The in-memory gclid is now persisted to _tb_gclid.
  3. Consent deny. The user clicks decline. tracker.updateConsent({ ad_storage: 'denied', ... }) is recorded. The in-memory gclid stays 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.

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.

  • 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), call gtag('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.trackConversion for a user who declined consent, it fires.