Your Framer Cookie Banner Looks Compliant. It Isn't.

A step-by-step guide to wiring up GTM, GA4, Hotjar, and Bloomreach Engagement so they only fire after consent, and fixing the bug Framer doesn't tell you about.
The problem
Framer has a built-in cookie banner. It looks compliant, it has a Reject all button, consent categories, and a Privacy Policy link. But out of the box it has two problems that make it non-compliant under GDPR:
Problem 1. Tools load before consent is read.
When a returning visitor lands on your site, Framer loads the stored consent asynchronously. This means GTM fires before the consent state is known, and your analytics tools load before they should.
Problem 2. Reject all contradicts the banner's own UI for necessary cookies.
Framer's banner settings include an option to mark Necessary cookies as always active and not revocable. The banner UI reflects that (Necessary is shown as "Always active"), but the consent signals Framer pushes to GTM do not. When a user clicks Reject all, Framer sends functionality_storage: denied and security_storage: denied anyway. These two signals cover basic site functionality (language preferences, UI state) and security concerns (session integrity, fraud prevention), which is exactly what the banner promised the user would stay on. The UI and the signal disagree, and any tool in GTM that respects these two signals will behave as if Necessary was revoked.
This guide walks through the exact GTM setup that fixes both problems.
What you need
Framer site with a custom domain
Google Tag Manager (GTM)
GA4 connected via GTM
Hotjar (optional but used as an example)
Bloomreach Engagement / Exponea (optional but used as an example)
The pattern works for any analytics or personalization tool. Hotjar and Bloomreach are just the examples we use here.
Step 1: Enable Framer's cookie banner
In Framer go to Site Settings → Cookie Banner → Enable.
Configure four categories:
Category | Description | Toggle |
|---|---|---|
Necessary | Security and basic functionality | Always active (locked) |
Preferences | Personalized content and settings | Off by default |
Analytics | Performance tracking | Off by default |
Marketing | Ads personalization and tracking | Off by default |
Set the banner to show on first visit with Reject all, Customize, and Accept all options.
Step 2: Set consent defaults before GTM loads
In Framer go to Site Settings → Custom Code → Head.
Add this before your GTM snippet:
This runs before GTM loads and sets everything consent-gated to denied by default, while keeping functionality and security granted so basic site operation works before the user interacts with the banner. GTM will respect these defaults and block any tags that require consent. (personalization_storage is the seventh Consent Mode v2 signal, covering video recommendations and similar personalization; include it in defaults for completeness.)
Then add your GTM snippet immediately after:
Replace YOUR-GTM-ID with your GTM container ID.
Step 3: Create the two consent triggers in GTM
You need two triggers that everything runs through:
Trigger 1. DOM Ready
Type: DOM Ready
Fires on: All Pages
Name it:
DOM Ready
This handles returning visitors. By the time DOM Ready fires, Framer has already loaded the stored consent into the dataLayer.
Trigger 2: cookie_consent_update
Type: Custom Event
Event name:
cookie_consent_updateFires on: All Custom Events
Name it:
cookie_consent_update
This handles new visitors. Framer pushes this event when a user makes a consent choice. It also fires for returning visitors when Framer loads stored consent on page load.
Step 4: Fix the Reject all bug
This is the non-obvious part. Framer's banner settings let you mark Necessary as always active and not revocable, and the UI renders that correctly. The consent signals it pushes to GTM do not match that UI: on Reject all, Framer sends functionality_storage: denied and security_storage: denied alongside the tracking signals. The two stories the user sees (banner, stored consent) tell different things to any downstream tool that checks these signals. Until Framer reconciles them, you fix it in GTM.
Create a new tag in GTM:
Type: Custom HTML
Name:
Consent Fix — Functionality + Security Always GrantedHTML:
Trigger:
cookie_consent_update
This tag fires every time consent is updated (including when the user rejects everything) and forces the two necessary signals back to granted. It runs silently after Framer's consent update and aligns the runtime signal with what the banner UI already showed the user.
Without this fix, basic site functionality can break for users who click Reject all, even though your banner promised them Necessary cookies are always active.
Step 5: Configure your analytics and personalization tags
Hotjar and Bloomreach Engagement (or any analytics/personalization tool) should use the dual trigger pattern:
For each tag:
Add DOM Ready as a trigger
Add cookie_consent_update as a second trigger
In the tag settings, enable Additional Consent Checks (under Advanced Settings → Consent Settings) and require
analytics_storage
The consent check gates the tag, even if the trigger fires, the tag won't execute unless analytics_storage is granted.
The two triggers handle the two visitor states:
DOM Ready: returning visitor, stored consent already loaded by DOM Ready
cookie_consent_update: new visitor who just accepted, or returning visitor where Framer pushes consent during page load
GA4 / Google Tag does not need consent gating in the same way. Set it to fire on All Pages. GA4 with Consent Mode v2 handles its own consent signals and sends cookieless pings when consent is denied.
Step 6: Final GTM structure
Your Tags should look like this:
Tag | Triggers |
|---|---|
Google Tag (GA4) | All Pages |
Hotjar Tracking Code | cookie_consent_update + DOM Ready |
Bloomreach / Exponea Tag | cookie_consent_update + DOM Ready |
Consent Fix tag | cookie_consent_update |
Your Triggers should be:
All Pages (Page View)
DOM Ready
cookie_consent_update (Custom Event)
Step 7: Verify in GTM Preview mode
Open GTM → Preview → enter your site URL
Test as a new visitor (open in incognito):
On page load: Hotjar and Bloomreach should show as blocked by consent
After clicking Accept all:
cookie_consent_updatefires → both tags loadAfter clicking Reject all: both tags stay blocked, Consent Fix tag fires and grants functionality + security
Test as a returning visitor who previously accepted:
On DOM Ready: both tags should fire immediately without waiting for user interaction
Check the consent timeline in GTM Preview, you should see:
Consent Initialization → all denied
DOM Ready or cookie_consent_update → consent updated to granted
Tags fire after consent is confirmed
Step 8: Update your Privacy Policy
Add a Third-Party Services section to your Privacy Policy listing each tool, what it collects, when it activates (after consent), and a link to the tool's own privacy policy. At minimum cover:
Google Analytics 4
Google Tag Manager
Hotjar
Bloomreach Engagement (if used)
Cloudflare (infrastructure, no consent required)
Why this matters
The GDPR requirement is not just that you show a banner, it is that tools do not process personal data before consent is given. A banner that looks compliant but loads tracking scripts on page load is not compliant, regardless of how it looks to the user.
The setup in this guide ensures:
No tracking fires before consent is known
Returning visitors are tracked immediately without unnecessary blocking
Reject all genuinely blocks all tracking
Necessary functionality works regardless of consent choice
GA4 operates in cookieless mode when consent is denied, satisfying Google's Consent Mode v2 requirement
Framer-specific notes
Framer pushes
cookie_consent_updateto the dataLayer whenever consent changes: this is the key event your GTM setup relies on. This behavior was observed in early 2026; verify in DevTools → Application → dataLayer before relying on the event nameThe
functionality_storage/security_storageinconsistency described in Step 4 was present as of early 2026. Test whether it still exists in your version by opening DevTools, clicking Reject all, and inspecting the most recentconsentupdateentry in the dataLayerIf Framer aligns the signal with its banner UI in a future update, the Consent Fix tag does no harm: it just redundantly confirms what Framer already set correctly
Related manuals
Once your consent setup is working, the Bloomreach Engagement manuals show how to wire each BR component into it correctly:
Loading Bloomreach Engagement Outside GTM: how to load the BR SDK in parallel with the consent stack this manual sets up
Fix the Bloomreach Engagement SDK Double-Load Warning in GTM: the Custom Template + Consent Mode queue double-load gotcha
Bloomreach Engagement Experiments on Framer: Async vs Sync Mode: why the default BR snippet throws a service-worker 404 on Framer
The Bloomreach Engagement Console Error You Probably Don't Know You Have: the
window.garetry exception on GA4-only sitesCapturing GA4 Client ID in Bloomreach Engagement: the modern replacement for the legacy Track GA Cookies option
Custom Tracking Domain in Bloomreach Engagement: how to make BR cookies survive Safari ITP


