From 2853f2e74ba0c29d839ac179379b46f17b9993f4 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 17 Jul 2026 00:19:40 -0700 Subject: [PATCH 1/3] feat(landing): X pixel conversion tracking on landing pages --- apps/sim/app/(landing)/layout.tsx | 15 +++++++- .../sim/app/(landing)/x-page-view-tracker.tsx | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 apps/sim/app/(landing)/x-page-view-tracker.tsx diff --git a/apps/sim/app/(landing)/layout.tsx b/apps/sim/app/(landing)/layout.tsx index 740deccce9f..9fef6524507 100644 --- a/apps/sim/app/(landing)/layout.tsx +++ b/apps/sim/app/(landing)/layout.tsx @@ -6,9 +6,18 @@ import { isHosted } from '@/lib/core/config/env-flags' import { SITE_URL } from '@/lib/core/utils/urls' import { LandingShell } from '@/app/(landing)/components' import { HubspotPageViewTracker } from '@/app/(landing)/hubspot-page-view-tracker' +import { XPageViewTracker } from '@/app/(landing)/x-page-view-tracker' const HUBSPOT_SCRIPT_SRC = 'https://js-na2.hs-scripts.com/246720681.js' as const +const X_PIXEL_ID = 'q5xbl' as const + +/** X (Twitter) conversion tracking base code — loads uwt.js and fires the initial PageView. */ +const X_PIXEL_BASE_CODE = `!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments); +},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js', +a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script'); +twq('config','${X_PIXEL_ID}');` + /** * Route-group layout for the entire landing family - the home page, platform and * solutions pages, pricing, legal, and the marketing subroutes (blog, models, @@ -33,12 +42,16 @@ export default function LandingLayout({ children }: { children: ReactNode }) { return ( {children} - {/* HubSpot tracking — hosted only */} + {/* HubSpot + X pixel tracking — hosted only */} {isHosted && ( <> + )} diff --git a/apps/sim/app/(landing)/x-page-view-tracker.tsx b/apps/sim/app/(landing)/x-page-view-tracker.tsx new file mode 100644 index 00000000000..a877004cde2 --- /dev/null +++ b/apps/sim/app/(landing)/x-page-view-tracker.tsx @@ -0,0 +1,37 @@ +'use client' + +import { useEffect } from 'react' +import { usePathname, useSearchParams } from 'next/navigation' + +declare global { + interface Window { + twq?: (...args: unknown[]) => void + } +} + +// next/script dedupes by id and never reloads on remount, so this must be +// module-scope (not a ref) to survive LandingLayout unmounting/remounting. +let hasTrackedInitialPageView = false + +/** + * The X pixel base code only auto-tracks the first page load; LandingLayout + * persists across client-side navigations, so the pixel never sees the rest. + * Re-fires the pixel's PageView via `twq('config', ...)` on every navigation + * after the first. + */ +export function XPageViewTracker() { + const pathname = usePathname() + const searchParams = useSearchParams() + const query = searchParams.toString() + + useEffect(() => { + if (!hasTrackedInitialPageView) { + hasTrackedInitialPageView = true + return + } + + window.twq?.('config', 'q5xbl') + }, [pathname, query]) + + return null +} From 39f9b109116b052bb04c87f8f89310166daf4be8 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 17 Jul 2026 00:25:01 -0700 Subject: [PATCH 2/3] fix(landing): dedupe X pixel initial PageView by URL to survive Strict Mode effect replay --- apps/sim/app/(landing)/x-page-view-tracker.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/(landing)/x-page-view-tracker.tsx b/apps/sim/app/(landing)/x-page-view-tracker.tsx index a877004cde2..e0f381c17c4 100644 --- a/apps/sim/app/(landing)/x-page-view-tracker.tsx +++ b/apps/sim/app/(landing)/x-page-view-tracker.tsx @@ -11,7 +11,9 @@ declare global { // next/script dedupes by id and never reloads on remount, so this must be // module-scope (not a ref) to survive LandingLayout unmounting/remounting. -let hasTrackedInitialPageView = false +// Keyed by URL (not a boolean) so Strict Mode effect replays and same-URL +// remounts never re-fire a PageView already counted for that URL. +let lastTrackedUrl: string | null = null /** * The X pixel base code only auto-tracks the first page load; LandingLayout @@ -25,11 +27,15 @@ export function XPageViewTracker() { const query = searchParams.toString() useEffect(() => { - if (!hasTrackedInitialPageView) { - hasTrackedInitialPageView = true + const url = query ? `${pathname}?${query}` : pathname + + if (lastTrackedUrl === null) { + lastTrackedUrl = url return } + if (url === lastTrackedUrl) return + lastTrackedUrl = url window.twq?.('config', 'q5xbl') }, [pathname, query]) From 46981441412b6827e72bf5c42167a33a524631c4 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 17 Jul 2026 00:28:55 -0700 Subject: [PATCH 3/3] fix(landing): scope X pixel URL dedupe to the tracker instance so same-URL returns to landing still track --- .../sim/app/(landing)/x-page-view-tracker.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/sim/app/(landing)/x-page-view-tracker.tsx b/apps/sim/app/(landing)/x-page-view-tracker.tsx index e0f381c17c4..5db1e0439fd 100644 --- a/apps/sim/app/(landing)/x-page-view-tracker.tsx +++ b/apps/sim/app/(landing)/x-page-view-tracker.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { usePathname, useSearchParams } from 'next/navigation' declare global { @@ -11,9 +11,7 @@ declare global { // next/script dedupes by id and never reloads on remount, so this must be // module-scope (not a ref) to survive LandingLayout unmounting/remounting. -// Keyed by URL (not a boolean) so Strict Mode effect replays and same-URL -// remounts never re-fire a PageView already counted for that URL. -let lastTrackedUrl: string | null = null +let hasTrackedInitialPageView = false /** * The X pixel base code only auto-tracks the first page load; LandingLayout @@ -26,16 +24,21 @@ export function XPageViewTracker() { const searchParams = useSearchParams() const query = searchParams.toString() + // Instance-scoped (not module-scoped) so a Strict Mode replay of this + // mount's effect is skipped, while a fresh mount — returning to the landing + // layout from the app — starts empty and tracks the view again. + const lastTrackedUrlRef = useRef(null) + useEffect(() => { const url = query ? `${pathname}?${query}` : pathname + if (lastTrackedUrlRef.current === url) return + lastTrackedUrlRef.current = url - if (lastTrackedUrl === null) { - lastTrackedUrl = url + if (!hasTrackedInitialPageView) { + hasTrackedInitialPageView = true return } - if (url === lastTrackedUrl) return - lastTrackedUrl = url window.twq?.('config', 'q5xbl') }, [pathname, query])