From b325684f19cd5aabcec903bfce0781e6bf568911 Mon Sep 17 00:00:00 2001 From: Mikaal Naik Date: Fri, 14 Aug 2026 16:09:31 -0400 Subject: [PATCH 1/2] tracking pixel --- src/app/layout.tsx | 2 ++ src/components/XPixel.tsx | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/components/XPixel.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 17b6b45..ea3a66a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,6 +9,7 @@ import { Toaster } from "sonner"; import { SubscribeModal } from "@/components/subscribe"; import { IdentifyUser } from "@/components/auth/IdentifyUser"; import { HubspotTracking } from "@/components/HubspotTracking"; +import { XPixel } from "@/components/XPixel"; const GA_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID; @@ -77,6 +78,7 @@ export default function RootLayout({ + ); diff --git a/src/components/XPixel.tsx b/src/components/XPixel.tsx new file mode 100644 index 0000000..1c845a4 --- /dev/null +++ b/src/components/XPixel.tsx @@ -0,0 +1,23 @@ +import Script from "next/script"; + +/* X (Twitter) Ads universal website tag. `twq('config', )` loads the + tag and reports the initial page load; conversion events are then reported + with twq('event', 'tw--', {...}) from wherever they happen. + + Production only, so localhost and preview deployments stay out of the ad + data. To check the tag locally, temporarily drop the NODE_ENV guard. */ + +const X_PIXEL_ID = "re2t6"; + +export function XPixel() { + if (process.env.NODE_ENV !== "production") return null; + + return ( + + ); +} From 57bfc16b58e116ffb61c4bb2400d0806645a5cab Mon Sep 17 00:00:00 2001 From: Mikaal Naik Date: Thu, 20 Aug 2026 11:25:51 -0400 Subject: [PATCH 2/2] Report X pixel page views on client navigations uwt.js installs no history listener, so App Router route changes went unreported. Mirror the HubSpot tracker: watch pathname/searchParams and re-call twq('config', ...), which fires a fresh page-load beacon on every invocation (no init-once guard in the tag). Co-Authored-By: Claude Opus 5 --- src/components/XPixel.tsx | 52 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/components/XPixel.tsx b/src/components/XPixel.tsx index 1c845a4..9391f5f 100644 --- a/src/components/XPixel.tsx +++ b/src/components/XPixel.tsx @@ -1,23 +1,69 @@ +"use client"; + import Script from "next/script"; +import { usePathname, useSearchParams } from "next/navigation"; +import { Suspense, useEffect, useRef } from "react"; /* X (Twitter) Ads universal website tag. `twq('config', )` loads the tag and reports the initial page load; conversion events are then reported with twq('event', 'tw--', {...}) from wherever they happen. + uwt.js installs no history listener, so App Router navigations would + otherwise go unreported. Each route change re-calls twq('config', ...), + which sends a fresh page-load beacon every time it runs (the command is + 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 tag locally, temporarily drop the NODE_ENV guard. */ const X_PIXEL_ID = "re2t6"; +declare global { + interface Window { + twq?: (...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.twq?.("config", X_PIXEL_ID); + }, [pathname, searchParams]); + + return null; +} + export function XPixel() { if (process.env.NODE_ENV !== "production") return null; return ( - + + {/* useSearchParams requires a Suspense boundary in the App Router */} + + + + ); }