You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, and thanks for Nuxt Scripts! We use the Meta Pixel registry with defaultConsent: 'denied' and call consent.grant() once our cookie banner knows the visitor has agreed. When that grant happens before fbevents.js has loaded (for example, the decision is already stored in a cookie), the pixel never sends anything: no config request and no /tr.
From what we can see, clientInit queues ['consent', 'revoke'] ahead of init and PageView, and a grant made before load is appended to the same queue:
When fbevents loads, it replays that queue in order and stops at the revoke. It only lets consent commands skip its lock when they arrive live through fbq.callMethod, so the queued grant behind the revoke is never reached. Any fbq('consent', 'grant') made after load does unlock it, which is why an in-session "Accept" click appears to work.
We might be missing an intended usage here, so happy to be corrected.
Open the preview and it prints the result: the fbq.queue contents and whether a PageView request was sent.
The page is the snippet below on @nuxt/scripts 1.3.9 and Nuxt 4.5.2. It uses scriptOptions: { bundle: false } only because WebContainers can't fetch fbevents.js at build time.
<script setup lang="ts">
const { consent } =useScriptMetaPixel({ id: '1234567890', defaultConsent: 'denied', scriptOptions: { bundle: false },})// Consent already known, e.g. from a cookie, before the pixel has loaded.if (import.meta.client)consent?.grant()
</script>
Result: fbevents 2.9.408 loads, fbq.queue stays at ["init,1234567890", "track,PageView", "consent,grant"], and no /tr request is made.
We see the same thing locally against main (4bb52cc) in the playground. Tracing window.fbq.queue in Chrome there:
before fbevents loads: consent,revoke / init / track,PageView / consent,grant
about 100 ms later, fbevents sets callMethod
after load: the queue is stuck at init / track,PageView / consent,grant, and every later event is appended behind it
no request to signals/config or /tr
🌈 Expected behavior
A grant made before the pixel loads applies before init, the same as if defaultConsent had been 'granted': the config request and the PageView go out, and later events are sent.
The docs describe this exact flow under Consent Mode (defaultConsent: 'denied', then consent.grant()).
ℹ️ Additional context
Seen on 1.3.9 and on main (4bb52cc); the stub is the same in both.
The same page on the StackBlitz starter's default @nuxt/scripts 0.10.5 does send the PageView. As far as we can tell, that is only because defaultConsent and the consent API don't exist there yet, so no revoke is queued. They first appear in 1.0.0-rc.10 (from feat: vendor-native consent controls for consent-aware scripts #712), and the stub has queued the revoke this way since then.
A revoke made before load has the mirror problem: it is appended after the queued PageView, so that PageView is still sent once the pixel loads.
We have a small fix with tests that we would be glad to open as a PR: before fbevents loads, a consent call replaces the pending consent state at the head of the queue instead of being appended. With it, the same repro sends the config request, the PageView and later events.
I investigated this with help from an AI coding assistant (Claude Code), which also drafted this text. I reviewed it, and the results above come from real runs.
🐛 The bug
Hi, and thanks for Nuxt Scripts! We use the Meta Pixel registry with
defaultConsent: 'denied'and callconsent.grant()once our cookie banner knows the visitor has agreed. When that grant happens before fbevents.js has loaded (for example, the decision is already stored in a cookie), the pixel never sends anything: no config request and no/tr.From what we can see,
clientInitqueues['consent', 'revoke']ahead ofinitandPageView, and a grant made before load is appended to the same queue:When fbevents loads, it replays that queue in order and stops at the revoke. It only lets
consentcommands skip its lock when they arrive live throughfbq.callMethod, so the queued grant behind the revoke is never reached. Anyfbq('consent', 'grant')made after load does unlock it, which is why an in-session "Accept" click appears to work.We might be missing an intended usage here, so happy to be corrected.
🛠️ To reproduce
https://stackblitz.com/edit/nuxt-starter-j6imudo3?file=pages%2Findex.vue
Open the preview and it prints the result: the
fbq.queuecontents and whether aPageViewrequest was sent.The page is the snippet below on @nuxt/scripts 1.3.9 and Nuxt 4.5.2. It uses
scriptOptions: { bundle: false }only because WebContainers can't fetch fbevents.js at build time.Result: fbevents 2.9.408 loads,
fbq.queuestays at["init,1234567890", "track,PageView", "consent,grant"], and no/trrequest is made.We see the same thing locally against
main(4bb52cc) in the playground. Tracingwindow.fbq.queuein Chrome there:consent,revoke/init/track,PageView/consent,grantcallMethodinit/track,PageView/consent,grant, and every later event is appended behind itsignals/configor/tr🌈 Expected behavior
A grant made before the pixel loads applies before
init, the same as ifdefaultConsenthad been'granted': the config request and thePageViewgo out, and later events are sent.The docs describe this exact flow under Consent Mode (
defaultConsent: 'denied', thenconsent.grant()).ℹ️ Additional context
main(4bb52cc); the stub is the same in both.PageView. As far as we can tell, that is only becausedefaultConsentand theconsentAPI don't exist there yet, so no revoke is queued. They first appear in 1.0.0-rc.10 (from feat: vendor-native consent controls for consent-aware scripts #712), and the stub has queued the revoke this way since then.PageView, so thatPageViewis still sent once the pixel loads.PageViewand later events.I investigated this with help from an AI coding assistant (Claude Code), which also drafted this text. I reviewed it, and the results above come from real runs.