From f5adf5316c1bac5d5c6078586a8f70f25e05871e Mon Sep 17 00:00:00 2001 From: Mikaal Naik Date: Thu, 20 Aug 2026 13:23:10 -0400 Subject: [PATCH] Install the Meta pixel Add the Meta (Facebook) pixel alongside the existing X pixel, following the same pattern: the base snippet reports the initial page load, and a client tracker re-fires PageView on App Router navigations since fbevents.js installs no history listener. Production only, so localhost and preview deployments stay out of the ad data. Co-Authored-By: Claude Opus 5 --- src/app/layout.tsx | 2 + src/components/MetaPixel.tsx | 86 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/components/MetaPixel.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ea3a66a..ec595ea 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -10,6 +10,7 @@ import { SubscribeModal } from "@/components/subscribe"; import { IdentifyUser } from "@/components/auth/IdentifyUser"; import { HubspotTracking } from "@/components/HubspotTracking"; import { XPixel } from "@/components/XPixel"; +import { MetaPixel } from "@/components/MetaPixel"; const GA_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID; @@ -79,6 +80,7 @@ export default function RootLayout({ + ); diff --git a/src/components/MetaPixel.tsx b/src/components/MetaPixel.tsx new file mode 100644 index 0000000..aa9a486 --- /dev/null +++ b/src/components/MetaPixel.tsx @@ -0,0 +1,86 @@ +"use client"; + +import Script from "next/script"; +import { usePathname, useSearchParams } from "next/navigation"; +import { Suspense, useEffect, useRef } from "react"; + +/* Meta (Facebook) pixel. The base snippet loads fbevents.js and reports the + initial page load with fbq('track', 'PageView'); conversions are reported + with fbq('track', '', {...}) from wherever they happen. + + fbevents.js installs no history listener, so App Router navigations would + otherwise go unreported. Each route change re-fires the PageView track + (safe to repeat, and queues before the script finishes loading). + + Production only, so localhost and preview deployments stay out of the ad + data. To check the pixel locally, temporarily drop the NODE_ENV guard. */ + +const META_PIXEL_ID = "1782353696227901"; + +declare global { + interface Window { + fbq?: (...args: unknown[]) => void; + } +} + +function PageViewTracker() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + // last path reported; doubles as the initial-load marker so the first + // render (already tracked by the inline snippet) and strict-mode effect + // re-runs don't double-count + const lastPath = useRef(null); + + useEffect(() => { + const query = searchParams?.toString(); + const path = query ? `${pathname}?${query}` : pathname; + + if (lastPath.current === null) { + lastPath.current = path; + return; + } + if (lastPath.current === path) return; + lastPath.current = path; + + window.fbq?.("track", "PageView"); + }, [pathname, searchParams]); + + return null; +} + +export function MetaPixel() { + if (process.env.NODE_ENV !== "production") return null; + + return ( + <> + + + {/* useSearchParams requires a Suspense boundary in the App Router */} + + + + + ); +}