Framer’s Cookie Banner Denies the Cookies It Calls Always Active

Framer’s Cookie Banner Denies the Cookies It Calls Always Active

Framer’s Cookie Banner Denies the Cookies It Calls Always Active

Framer’s cookie banner marks Necessary cookies as always active and gives the visitor no toggle for them. Clicking Reject all sets them to false anyway, the two Consent Mode signals behind them go denied, and every later page load replays that. Here is the behavior across twenty-six tested states with no fixes present, the code that causes it, and what to add.

This is technical implementation guidance, not legal advice. Confirm your own consent setup with qualified counsel.

What the banner promises

Framer’s built-in banner is the consent management platform on a lot of Framer sites, so what it records is what every downstream tag sees. Open Cookie Settings and choose Customize. Preferences, Analytics and Marketing each get a toggle. Necessary gets the words Always active and no control at all. The description underneath reads "Enables security and basic functionality."

A visitor reading that screen would conclude the category cannot be switched off. The interface is telling them so.

One click, before and after

On a clean visit, with the banner showing its three buttons and nothing else touched, here is localStorage either side of a single click on Reject all:

BEFORE  {"analytics":false,"marketing":false,"necessary":true, "preferences":false}
AFTER   {"analytics":false,"marketing":false,"necessary":false,"preferences":false}
BEFORE  {"analytics":false,"marketing":false,"necessary":true, "preferences":false}
AFTER   {"analytics":false,"marketing":false,"necessary":false,"preferences":false}
BEFORE  {"analytics":false,"marketing":false,"necessary":true, "preferences":false}
AFTER   {"analytics":false,"marketing":false,"necessary":false,"preferences":false}

necessary went from true to false. The visitor was never shown a control for it.

Framer maps that field onto two Consent Mode v2 signals, so the next push carries functionality_storage: denied and security_storage: denied. Those cover language preferences, interface state and session integrity. They are the signals the banner just promised would stay on.

Three lines of Framer’s code

The reducer that handles the click, which writes all four booleans without checking any of them:

case `rejectAll`: return { ...e, sync:!0, dismissed:!0,
  modes:{ analytics:!1, marketing:!1, necessary:!1, preferences:!1 } };
case `rejectAll`: return { ...e, sync:!0, dismissed:!0,
  modes:{ analytics:!1, marketing:!1, necessary:!1, preferences:!1 } };
case `rejectAll`: return { ...e, sync:!0, dismissed:!0,
  modes:{ analytics:!1, marketing:!1, necessary:!1, preferences:!1 } };

The mapper that turns those booleans into signals:

function Ci(e){ return {
  functionality_storage: e.necessary ? `granted` : `denied`,
  security_storage:      e.necessary ? `granted` : `denied`,
  ...
function Ci(e){ return {
  functionality_storage: e.necessary ? `granted` : `denied`,
  security_storage:      e.necessary ? `granted` : `denied`,
  ...
function Ci(e){ return {
  functionality_storage: e.necessary ? `granted` : `denied`,
  security_storage:      e.necessary ? `granted` : `denied`,
  ...

And the flag that draws the "Always active" label:

necessary:{ description:`Enables security and basic functionality.`,
            optional:!1, title:`Necessary` }
necessary:{ description:`Enables security and basic functionality.`,
            optional:!1, title:`Necessary` }
necessary:{ description:`Enables security and basic functionality.`,
            optional:!1, title:`Necessary` }

That optional value is read in exactly one place, where it is handed to the row renderer to decide whether to draw a toggle. Neither the reducer nor the mapper ever sees it. The interface knows the category is locked. The consent state machine does not.

Why a reload never recovers

The obvious question is why a fresh visitor is fine and a reload after Reject all is not, when neither of them involves a click. The answer is that a fresh visitor never reads storage. (The full consent stack that repairs this covers both paths.)

function d(){
  let e = localStorage.getItem('framerCookiesConsentMode'),
      n = localStorage.getItem('framerCookiesDismissed'),
      r = localStorage.getItem('framerCookiesAutoAccepted'),
      i = n !== null,
      a = r !== null;
  o({ type:`initFromLocalStorage`, dismissed:i, autoAccepted:a,
      modes: e !== null && (i || a) ? vi(e, ) : t });   //  t = the defaults
}
function d(){
  let e = localStorage.getItem('framerCookiesConsentMode'),
      n = localStorage.getItem('framerCookiesDismissed'),
      r = localStorage.getItem('framerCookiesAutoAccepted'),
      i = n !== null,
      a = r !== null;
  o({ type:`initFromLocalStorage`, dismissed:i, autoAccepted:a,
      modes: e !== null && (i || a) ? vi(e, ) : t });   //  t = the defaults
}
function d(){
  let e = localStorage.getItem('framerCookiesConsentMode'),
      n = localStorage.getItem('framerCookiesDismissed'),
      r = localStorage.getItem('framerCookiesAutoAccepted'),
      i = n !== null,
      a = r !== null;
  o({ type:`initFromLocalStorage`, dismissed:i, autoAccepted:a,
      modes: e !== null && (i || a) ? vi(e, ) : t });   //  t = the defaults
}

Read the last line as: use the stored value only if something is stored and the visitor has answered. Otherwise use the component defaults, which are necessary: true in both the EU and world sets.

So the defaults are a first-visit fallback, not a safety net. framerCookiesDismissed is the switch that retires them, and Reject all sets it in the same action that writes necessary: false.

We confirmed the flag is what decides it. Same stored consent object both times, only the answered flag differs:

localStorage

Framer’s push

consent object all false, framerCookiesDismissed: "true"

f=denied s=denied

same consent object, framerCookiesDismissed removed

f=granted s=granted

Deleting only the answered flag brought both signals back to granted, and Framer rewrote necessary to true in storage on the next load.

How we tested it

Two runs of the same thirteen situations on a branch build, one per banner configuration, with every fix removed.

The head consent snippet was scoped away and the banner was pointed at a container ID that does not exist. gtm.js returned nothing, window.google_tag_manager stayed empty, and no tag could run. Every entry in the dataLayer was therefore the banner’s own. We verified that at the start of each run rather than assuming it.

For each situation we recorded the four stored booleans, every consent push, and the resulting state.

Results

f and s are functionality_storage and security_storage, the two signals the banner calls Necessary. Stored values are shown as necessary / preferences / analytics / marketing.

Necessary set to Always active, no toggle shown

#

What the visitor does

stored

f + s

1

Lands, banner shown, clicks nothing

ON / off / off / off

granted

2

First visit, Reject all

off / off / off / off

denied

3

Reload

off / off / off / off

denied

4

Accept all

ON / ON / ON / ON

granted

5

Reload

ON / ON / ON / ON

granted

6

Reopens settings, Reject all

off / off / off / off

denied

7

Reload

off / off / off / off

denied

8

Customize, Analytics on

off / off / ON / off

denied

9

Reload

off / off / ON / off

denied

10

Customize, Marketing on instead

off / off / off / ON

denied

11

Reload

off / off / off / ON

denied

12

Tries to switch Necessary back on

no toggle exists

denied

13

Accept all

ON / ON / ON / ON

granted

Necessary set to optional, toggle shown

#

What the visitor does

stored

f + s

1

Lands, banner shown, clicks nothing

ON / off / off / off

granted

2

First visit, Reject all

off / off / off / off

denied

3

Reload

off / off / off / off

denied

4

Accept all

ON / ON / ON / ON

granted

5

Reload

ON / ON / ON / ON

granted

6

Reopens settings, Reject all

off / off / off / off

denied

7

Reload

off / off / off / off

denied

8

Customize, Analytics on

off / off / ON / off

denied

9

Reload

off / off / ON / off

denied

10

Customize, Marketing on instead

off / off / off / ON

denied

11

Reload

off / off / off / ON

denied

12

Switches Necessary back on, saves

ON / off / off / ON

granted

13

Reload

ON / off / off / ON

granted

What the results say

It starts on the first click. Row 2 is a first-time visitor who reads the banner and declines. No revoke, no second visit, no edge case.

It survives every reload. Rows 3, 7, 9 and 11 all begin from necessary: false in storage, and Framer replays it. The banner does not reappear, because the visitor has already answered.

Partial consent does not help. Rows 8 through 11 are a visitor changing their mind twice. necessary stays false through all of it.

The two configurations differ in exactly one row. Rows 2 through 11 are identical. Only row 12 changes: with Always active on, there is no toggle and the visitor is stuck; with the toggle shown, they can switch it back on and the signals recover. So "Always active" does not keep the category on. It removes the only control that could turn it back on.

The one exit is Accept all. Row 13. A visitor who wants necessary cookies but not tracking cannot express that once they have declined.

What to add

Two lines. They belong in the GTM tag that replays the visitor’s stored choice, alongside the five signals it already restores:

'functionality_storage': 'granted',
'security_storage':      'granted'
'functionality_storage': 'granted',
'security_storage':      'granted'
'functionality_storage': 'granted',
'security_storage':      'granted'

That tag runs on the Consent Initialization trigger, which is the only one that fires on every page load rather than only when something changes. Adding the two grants there is what closes the hole, because the cookie_consent_update event Framer pushes is guarded, so a visitor who declined everything never triggers it and an update-only fix never runs for them.

A second tag on cookie_consent_update catches the click itself, in the moment, before any page load happens. And a deny-first default in the head, issued before GTM loads, grants the two necessary signals and denies the five gated ones; because gtag ignores a default that arrives after the container has started, Framer’s later denial is discarded outright.

Both tag bodies, their triggers and the head snippet are in Your Framer Cookie Banner Looks Compliant. It Isn’t., Step 2 for the head default, Step 3 for the restore tag, Step 5 for the click fix. They are maintained there rather than repeated here.

Between them, every one of the thirteen situations above lands on granted for the two necessary signals and on the visitor’s actual answer for the other five.

Check your own site in a minute

Open the console on any page of your Framer site and force the state:

localStorage.setItem('framerCookiesConsentMode',
  '{"analytics":false,"marketing":false,"necessary":false,"preferences":false}');
localStorage.setItem('framerCookiesDismissed','true');
location.reload();
localStorage.setItem('framerCookiesConsentMode',
  '{"analytics":false,"marketing":false,"necessary":false,"preferences":false}');
localStorage.setItem('framerCookiesDismissed','true');
location.reload();
localStorage.setItem('framerCookiesConsentMode',
  '{"analytics":false,"marketing":false,"necessary":false,"preferences":false}');
localStorage.setItem('framerCookiesDismissed','true');
location.reload();

Then read the pushes:

window.dataLayer.filter(function(e){ try { return e[0] === 'consent'; } catch(x) { return false; } })
window.dataLayer.filter(function(e){ try { return e[0] === 'consent'; } catch(x) { return false; } })
window.dataLayer.filter(function(e){ try { return e[0] === 'consent'; } catch(x) { return false; } })

Look at the last entry that mentions functionality_storage. If it says denied, you have the problem. Clear both keys afterwards to reset yourself to a fresh visitor.

One thing not to do

Do not clear the GTM ID on the banner component to stop it pushing. That ID gates the component’s entire consent path, so clearing it stops the consent default, the updates and the cookie_consent_update event together. The banner still renders, the visitor’s choice still reaches localStorage, and GTM never hears any of it. Every consent-gated tag then stays blocked for everyone, including visitors who accepted.

Recorded against the Framer cookie component as served on September 6, 2026, on a branch build of our own site, container GTM-P327SKL7. Framer shipped a new runtime build during our testing, and the three pieces of code above came through it unchanged, so the behavior is not tied to one build. It is still a single site on a single date. Framer can change the component, so verify before you rely on any of it.

Read next

WRITTEN BY

Jan Sacha, co-founder of Asteroad

Jan Sacha

Co-Founder · Bloomreach Consultant

Bloomreach Engagement consultant, in his third year at Asteroad running daily CDP and marketing automation work for European clients. Former Exponea enterprise consultant and CEO of Digiline.

Rather have us set it up for you?

Every manual here comes from real implementation work. If you would rather hand it off, tell us what you are trying to set up. We will listen first. No pitch, no commitment.