meta pixel: default to disablePushState = true

Breaks SvelteKit SPA which doesn't allow use of history API, requiring
its own wrapper to be used instead.
This commit is contained in:
Elijah Duffy
2025-12-17 22:19:05 -08:00
parent 095462c80d
commit 9a72280737
2 changed files with 35 additions and 1 deletions

View File

@@ -340,6 +340,11 @@ export interface FBQ {
// consent and LDU // consent and LDU
(cmd: 'consent', state: 'grant' | 'revoke'): void; (cmd: 'consent', state: 'grant' | 'revoke'): void;
(cmd: 'dataProcessingOptions', options: string[], countryCode?: number, stateCode?: number): void; (cmd: 'dataProcessingOptions', options: string[], countryCode?: number, stateCode?: number): void;
/** Prevent automatic listening to history.pushState/popstate */
disablePushState?: boolean;
/** Allow duplicate page view events (legacy / undocumented behavior) */
allowDuplicatePageViews?: boolean;
} }
declare global { declare global {

View File

@@ -9,22 +9,48 @@ type QueuedFBQ = ((...args: unknown[]) => void) & {
loaded?: boolean; loaded?: boolean;
version?: string; version?: string;
push?: unknown; push?: unknown;
disablePushState?: boolean;
allowDuplicatePageViews?: boolean;
}; };
/** /**
* Loads the Meta Pixel script and configures the `fbq` function to queue * Loads the Meta Pixel script and configures the `fbq` function to queue
* commands until the script is fully loaded. You may optionally await the * commands until the script is fully loaded. You may optionally await the
* returned Promise to ensure the script has loaded before proceeding. * returned Promise to ensure the script has loaded before proceeding.
*
* Options:
* - `disablePushState` (default: true) — when true, sets
* `window.fbq.disablePushState = true` before the pixel script loads so the
* pixel does not auto-listen to `history.pushState`/`popstate` (recommended
* for SPA frameworks like Svelte).
* - `allowDuplicatePageViews` (default: false) — when true, sets
* `window.fbq.allowDuplicatePageViews = true` on the stub.
*/ */
export const loadMetaPixel = (): Promise<void> => { export const loadMetaPixel = (opts?: {
disablePushState?: boolean;
allowDuplicatePageViews?: boolean;
}): Promise<void> => {
// Make sure we're using the browser // Make sure we're using the browser
if (!browser || !window) { if (!browser || !window) {
return Promise.reject(new Error(`Not in browser, can't access window`)); return Promise.reject(new Error(`Not in browser, can't access window`));
} }
// Default behavior: disable pushState handling since Svelte apps manage
// navigation themselves and Meta's auto-patching of history APIs can
// cause duplicate/incorrect pageview events. Consumers can pass
// `opts.disablePushState = false` to opt out.
const disablePushState = opts?.disablePushState ?? true;
const allowDuplicatePageViews = opts?.allowDuplicatePageViews ?? false;
// If fbq is already defined, resolve immediately // If fbq is already defined, resolve immediately
const existing = window.fbq as QueuedFBQ | undefined; const existing = window.fbq as QueuedFBQ | undefined;
if (existing) { if (existing) {
// If the existing stub is present but hasn't set these flags yet, set
// them now so the loaded library (if it inspects them) sees intended
// behavior. Setting these is a no-op if initialization already
// completed.
if (disablePushState) existing.disablePushState = true;
if (allowDuplicatePageViews) existing.allowDuplicatePageViews = true;
const existingScript = getExistingScript(); const existingScript = getExistingScript();
if (existingScript) { if (existingScript) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -53,6 +79,9 @@ export const loadMetaPixel = (): Promise<void> => {
q.push = q; q.push = q;
q.loaded = true; q.loaded = true;
q.version = '2.0'; q.version = '2.0';
// set control flags on the stub before the meta script runs
if (disablePushState) q.disablePushState = true;
if (allowDuplicatePageViews) q.allowDuplicatePageViews = true;
window.fbq = q; window.fbq = q;
window._fbq = q; window._fbq = q;