We added a Meta Pixel Lead event to track a key user action. We deployed, opened Test Events in Meta Events Manager, and saw nothing. What followed was a chain of four failures that ended up shaping how we write tracking code at ReplyQ.
The setup: tracking across two pages
The event could not fire on the form page itself — the user navigates away immediately after submit. Classic approach: save a flag, fire the event on the destination page. Simple in theory. We fell four times.
Failure 1: fbq event_callback is not reliable
Meta's fbq supports a callback that fires when the event reaches Meta's servers. Our plan: wait for the callback, then navigate. Problem: the callback does not always fire — especially behind ad blockers and on slow connections. We dropped this approach entirely.
Failure 2: router.push breaks cross-page tracking
We switched to the sessionStorage flag pattern:
sessionStorage.setItem('signup_success', '1')
router.push('/destination')
Still nothing. The reason: in Next.js App Router, router.push is soft navigation — the URL changes but the page does not reload. A useEffect that already ran does not re-run. The fix:
window.location.href = '/destination'
Hard navigation. Full page reload. useEffect fires fresh. sessionStorage survives the transition.
Failure 3: fbq async load race condition
With hard navigation and sessionStorage in place — still nothing. Browser console showed typeof window.fbq === 'undefined' at useEffect mount time. The Pixel script loads asynchronously and was not ready yet.
Solution — a retry loop:
const fireLead = () => {
attempts++
if (typeof window.fbq !== 'function') {
if (attempts < 10) setTimeout(fireLead, 200)
return
}
window.fbq('track', 'Lead', { ... })
sessionStorage.removeItem('signup_success') // clean up AFTER firing
}
fireLead()
Key detail: clean the flag only after a successful fire, never before. Cleaning first means a retry is impossible if fbq fails.
Failure 4: a second signup path nobody audited
After all three fixes — still failing. Console logged:
Lead skipped - no signup marker
sessionStorage empty. No query param. How?
A single grep across the codebase:
grep -r "/destination-page" src/
Revealed two separate signup flows. The email/password path had all the fixes. The OAuth path called an older helper that did router.push('/destination') with no marker at all. The user was coming through OAuth. Every flag check was correctly failing because the flag was never set.
We fixed the OAuth path and added a third fallback:
const fromQuery = params.get('signup_success') === '1'
const fromSession = sessionStorage.getItem('signup_success') === '1'
const fromReferrer = document.referrer.includes('/signup')
const shouldFire = fromQuery || fromSession || fromReferrer
Three rules we kept
- Grep before every tracking fix. Always count how many code paths reach the destination page. One missed path breaks everything.
- router.push breaks cross-page tracking in Next.js App Router. For any event that needs to survive a page transition — use window.location.href.
- Three fallbacks beat one. query param + sessionStorage + referrer. If one fails, another saves the event.
The Lead event landed exactly as expected. The path to get there taught us more than a first-try success ever would have.
If you are building automation for your business and want to see the platform in action — schedule a free demo.