Google Consent Mode v2

Since March 2024, Google requires Consent Mode v2 signals (ad_user_data, ad_personalization) for EEA traffic. ConsentLoop implements the whole dance — defaults before tags load, updates after every choice — with one flag.

ConsentLoop.run({
  categories: { analytics: {}, marketing: {} },
  googleConsentMode: true,
});

What happens

  1. Immediately at run() (or the loader, with data-gcm): gtag('consent', 'default', …) is pushed with everything denied except security_storage, plus wait_for_update: 500. Returning visitors get their stored state baked into the default — no denied-window flicker.
  2. After every choice: gtag('consent', 'update', …) with the mapped state.
⚠️Load ConsentLoop before your gtag/GTM snippet in the document so defaults win the race. With defer on both, order in HTML is the order of execution.

Default mapping

CategoryConsent Mode keys
necessarysecurity_storage (always granted)
functionalityfunctionality_storage, personalization_storage
analyticsanalytics_storage
marketingad_storage, ad_user_data, ad_personalization

Custom categories? Provide your own map (unmapped keys stay denied):

googleConsentMode: {
  map: {
    analytics: ["analytics_storage"],
    embeds: ["ad_storage"],
    personalization: ["personalization_storage", "functionality_storage"],
  },
  waitForUpdate: 500,
}

Live inspector

This page runs ConsentLoop with googleConsentMode: true. Current state, straight from this page’s dataLayer:

dataLayer pushes

Putting it together with gtag

<!-- 1. ConsentLoop first: pushes the consent default -->
<script defer src="https://cdn.jsdelivr.net/npm/consentloop/dist/consentloop.loader.min.js" data-gcm></script>

<!-- 2. Then Google — ungated: Consent Mode governs it cookielessly -->
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }
  gtag("js", new Date());
  gtag("config", "G-XXXX");
</script>

Prefer no Google requests at all before consent? Gate the gtag script with data-consent="analytics" instead — Consent Mode then covers the case where it loads later. Both patterns are compliant; the gated one is stricter (no pings), Consent Mode-only preserves Google’s conversion modeling.