Skip to content

Commit 5f2e0ae

Browse files
committed
feat(landing): X pixel conversion tracking on landing pages
1 parent 86e6e1d commit 5f2e0ae

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

apps/sim/app/(landing)/layout.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@ import { isHosted } from '@/lib/core/config/env-flags'
66
import { SITE_URL } from '@/lib/core/utils/urls'
77
import { LandingShell } from '@/app/(landing)/components'
88
import { HubspotPageViewTracker } from '@/app/(landing)/hubspot-page-view-tracker'
9+
import { XPageViewTracker } from '@/app/(landing)/x-page-view-tracker'
910

1011
const HUBSPOT_SCRIPT_SRC = 'https://js-na2.hs-scripts.com/246720681.js' as const
1112

13+
const X_PIXEL_ID = 'q5xbl' as const
14+
15+
/** X (Twitter) conversion tracking base code — loads uwt.js and fires the initial PageView. */
16+
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);
17+
},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
18+
a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
19+
twq('config','${X_PIXEL_ID}');`
20+
1221
/**
1322
* Route-group layout for the entire landing family - the home page, platform and
1423
* solutions pages, pricing, legal, and the marketing subroutes (blog, models,
@@ -33,12 +42,16 @@ export default function LandingLayout({ children }: { children: ReactNode }) {
3342
return (
3443
<LandingShell>
3544
{children}
36-
{/* HubSpot tracking — hosted only */}
45+
{/* HubSpot + X pixel tracking — hosted only */}
3746
{isHosted && (
3847
<>
3948
<Script id='hs-script-loader' src={HUBSPOT_SCRIPT_SRC} strategy='afterInteractive' />
49+
<Script id='x-pixel-base' strategy='afterInteractive'>
50+
{X_PIXEL_BASE_CODE}
51+
</Script>
4052
<Suspense fallback={null}>
4153
<HubspotPageViewTracker />
54+
<XPageViewTracker />
4255
</Suspense>
4356
</>
4457
)}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { usePathname, useSearchParams } from 'next/navigation'
5+
6+
declare global {
7+
interface Window {
8+
twq?: (...args: unknown[]) => void
9+
}
10+
}
11+
12+
// next/script dedupes by id and never reloads on remount, so this must be
13+
// module-scope (not a ref) to survive LandingLayout unmounting/remounting.
14+
let hasTrackedInitialPageView = false
15+
16+
/**
17+
* The X pixel base code only auto-tracks the first page load; LandingLayout
18+
* persists across client-side navigations, so the pixel never sees the rest.
19+
* Re-fires the pixel's PageView via `twq('config', ...)` on every navigation
20+
* after the first.
21+
*/
22+
export function XPageViewTracker() {
23+
const pathname = usePathname()
24+
const searchParams = useSearchParams()
25+
const query = searchParams.toString()
26+
27+
useEffect(() => {
28+
if (!hasTrackedInitialPageView) {
29+
hasTrackedInitialPageView = true
30+
return
31+
}
32+
33+
window.twq?.('config', 'q5xbl')
34+
}, [pathname, query])
35+
36+
return null
37+
}

0 commit comments

Comments
 (0)