Skip to content

fix(fb-pixel): guard global fbq bootstrap so multiple pixel destinations don't conflict#3097

Open
sarmah-rup wants to merge 2 commits into
rudderlabs:developfrom
sarmah-rup:fix/meta-pixel-multiple-destinations
Open

fix(fb-pixel): guard global fbq bootstrap so multiple pixel destinations don't conflict#3097
sarmah-rup wants to merge 2 commits into
rudderlabs:developfrom
sarmah-rup:fix/meta-pixel-multiple-destinations

Conversation

@sarmah-rup

@sarmah-rup sarmah-rup commented Jul 8, 2026

Copy link
Copy Markdown

Fixes #2587

Symptom

When two Meta Pixel destinations are configured in device mode (different Pixel IDs), the browser console logs:

[Meta Pixel] - Multiple pixels with conflicting versions were detected on this page.

Only the first pixel ends up initialized (confirmed with the Meta Pixel Helper extension); the second one does not fire.

Root cause

FacebookPixel.init() runs the global fbq bootstrap unconditionally. The fbevents.js script is loaded only once, but each destination's init() re-runs the shim setup and overwrites the already-loaded SDK: it reassigns window._fbq, resets window.fbq.push/loaded/version and, critically, window.fbq.queue = []. Meta's real SDK sees its own fbq mutated back to the 2.0 bootstrap state and reports a version conflict, which breaks the second pixel.

Fix

I wrapped only the one-time shim bootstrap in an if (!window.fbq) { ... } guard, mirroring Meta's official snippet (if (f.fbq) return;). The per-destination calls stay outside the guard, so every destination still registers itself: fbq('set', 'autoConfig', ...), the advanced-mapping block, fbq('init', pixelId, ...) and the ScriptLoader(...) call all run for each instance. The result is a single global shim with both pixels initialized and no conflict warning.

Tests

Added a unit test in packages/analytics-js-integrations/__tests__/integrations/FacebookPixel/browser.test.js that initializes one destination, marks the shim (window.fbq.version + a queued event), initializes a second destination, and asserts the global shim and its queue are preserved while the second pixel still registers its own init. The full FacebookPixel suite passes (9/9), and I verified the new test fails against the old unguarded code, so it guards against regressions.

I'm happy to sign the CLA and to make any changes the team would like.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented the Meta Pixel browser shim from being re-initialized when multiple pixel destinations initialize on the same page.
    • Preserved any existing Pixel queue and browser state during subsequent initializations, while still registering each pixel’s initialization.
    • Ensured pushState/pageview-related flags are refreshed on every initialization call, even when the shim already exists.
  • Tests
    • Expanded browser integration coverage to verify multi-destination initialization and repeated init behavior.

@sarmah-rup sarmah-rup requested a review from a team as a code owner July 8, 2026 19:05
@contributor-support

Copy link
Copy Markdown

Thank you @sarmah-rup for contributing this PR.
Please sign the Contributor License Agreement (CLA) before merging.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ac48a851-a51a-4b25-8cb8-16f89a030ac1

📥 Commits

Reviewing files that changed from the base of the PR and between e2c3849 and 3c71a7c.

📒 Files selected for processing (2)
  • packages/analytics-js-integrations/__tests__/integrations/FacebookPixel/browser.test.js
  • packages/analytics-js-integrations/src/integrations/FacebookPixel/browser.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/analytics-js-integrations/tests/integrations/FacebookPixel/browser.test.js
  • packages/analytics-js-integrations/src/integrations/FacebookPixel/browser.js

📝 Walkthrough

Walkthrough

FacebookPixel now avoids resetting an existing global window.fbq shim on later init() calls, while still reapplying pageview and pushState flags each time. Tests cover shim preservation across multiple destinations and existing-shim flag updates.

Changes

Meta Pixel Bootstrap Guard

Layer / File(s) Summary
Bootstrap guard and verification tests
packages/analytics-js-integrations/src/integrations/FacebookPixel/browser.js, packages/analytics-js-integrations/__tests__/integrations/FacebookPixel/browser.test.js
init() now only bootstraps the fbq/_fbq shim when window.fbq is absent, and the Jest suite checks shim identity, queue preservation, and repeated flag assignment across multiple initializations.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DestinationA
  participant DestinationB
  participant WindowFbq

  DestinationA->>WindowFbq: init() checks if (!window.fbq)
  WindowFbq-->>DestinationA: not set, create shim
  DestinationA->>WindowFbq: register init command for pixelId A

  DestinationB->>WindowFbq: init() checks if (!window.fbq)
  WindowFbq-->>DestinationB: already set, skip shim reset
  DestinationB->>WindowFbq: reapply flags and register init command for pixelId B
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: guarding fbq bootstrap to avoid multi-destination conflicts.
Description check ✅ Passed The description includes the symptom, root cause, fix, and tests, so it is mostly complete despite minor template sections being omitted.
Linked Issues check ✅ Passed The guarded bootstrap and regression test directly address #2587 by preventing conflicting global fbq reinitialization across destinations.
Out of Scope Changes check ✅ Passed The only changes are the FacebookPixel source fix and its corresponding test, which are in scope for the reported bug.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a device-mode Meta (Facebook) Pixel integration issue where configuring multiple pixel destinations on the same page causes Meta’s SDK to detect a conflicting fbq version and prevents subsequent pixels from initializing. It does so by guarding the one-time global fbq shim bootstrap to avoid reinitializing/overwriting the global state when init() is called multiple times.

Changes:

  • Guard the global fbq shim bootstrap in FacebookPixel.init() so it only runs once per page.
  • Add a unit test ensuring the global shim/queue state is preserved when initializing a second destination while still registering the second pixel.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/analytics-js-integrations/src/integrations/FacebookPixel/browser.js Adds a !window.fbq guard around the one-time fbq shim bootstrap to avoid conflicting reinitialization across multiple destinations.
packages/analytics-js-integrations/tests/integrations/FacebookPixel/browser.test.js Adds a regression test covering multi-destination initialization behavior and shim preservation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +61 to +76
if (!window.fbq) {
window._fbq = function () {
if (window.fbq.callMethod) {
window.fbq.callMethod.apply(window.fbq, arguments);
} else {
window.fbq.queue.push(arguments);
}
};

window.fbq = window.fbq || window._fbq;
window.fbq.push = window.fbq;
window.fbq.loaded = true;
window.fbq.disablePushState = true; // disables automatic pageview tracking
window.fbq.allowDuplicatePageViews = true; // enables fb
window.fbq.version = '2.0';
window.fbq.queue = [];
window.fbq = window.fbq || window._fbq;
window.fbq.push = window.fbq;
window.fbq.loaded = true;
window.fbq.disablePushState = true; // disables automatic pageview tracking
window.fbq.allowDuplicatePageViews = true; // enables fb
window.fbq.version = '2.0';
window.fbq.queue = [];

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I moved disablePushState and allowDuplicatePageViews out of the one-time guard so they run on every init(), including when window.fbq was already bootstrapped by the host page or an earlier destination. Only the shim creation and version/queue setup (the parts that cause the version conflict) stay guarded. Added a test covering the pre-existing fbq case.

Comment on lines +88 to +94
describe('FacebookPixel multiple destinations bootstrap guard', () => {
const mockAnalytics = {};

beforeEach(() => {
delete window.fbq;
delete window._fbq;
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. I now capture the previous window.fbq and window._fbq in beforeEach and restore them in an afterEach, so this suite no longer leaks global state into the sibling describes.

Copilot AI review requested due to automatic review settings July 9, 2026 04:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG : Conflicting Versions with Multiple Meta Pixel Destinations

2 participants