Skip to content

Commit 2277834

Browse files
committed
feat(landing): add PostHog tracking for CTA clicks, demo requests, and prompt submissions
1 parent 8b1d749 commit 2277834

File tree

9 files changed

+51
-4
lines changed

9 files changed

+51
-4
lines changed

apps/sim/app/(landing)/components/demo-request/demo-request-modal.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
type DemoRequestPayload,
1919
demoRequestSchema,
2020
} from '@/app/(landing)/components/demo-request/consts'
21+
import { captureClientEvent } from '@/lib/posthog/client'
2122

2223
interface DemoRequestModalProps {
2324
children: React.ReactNode
@@ -163,6 +164,9 @@ export function DemoRequestModal({ children, theme = 'dark' }: DemoRequestModalP
163164
}
164165

165166
setSubmitSuccess(true)
167+
captureClientEvent('landing_demo_request_submitted', {
168+
company_size: parsed.data.companySize,
169+
})
166170
} catch (error) {
167171
setSubmitError(
168172
error instanceof Error

apps/sim/app/(landing)/components/footer/footer-cta.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { ArrowUp } from 'lucide-react'
55
import Link from 'next/link'
66
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
77
import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder'
8+
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
9+
import { captureClientEvent } from '@/lib/posthog/client'
810

911
const MAX_HEIGHT = 120
1012

@@ -21,6 +23,7 @@ export function FooterCTA() {
2123

2224
const handleSubmit = useCallback(() => {
2325
if (isEmpty) return
26+
captureClientEvent('landing_prompt_submitted', {})
2427
landingSubmit(inputValue)
2528
}, [isEmpty, inputValue, landingSubmit])
2629

@@ -94,12 +97,14 @@ export function FooterCTA() {
9497
target='_blank'
9598
rel='noopener noreferrer'
9699
className={`${CTA_BUTTON} border-[var(--landing-border-strong)] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]`}
100+
onClick={() => trackLandingCta({ label: 'Docs', section: 'footer_cta', destination: 'https://docs.sim.ai' })}
97101
>
98102
Docs
99103
</a>
100104
<Link
101105
href='/signup'
102106
className={`${CTA_BUTTON} gap-2 border-white bg-white text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
107+
onClick={() => trackLandingCta({ label: 'Get started', section: 'footer_cta', destination: '/signup' })}
103108
>
104109
Get started
105110
</Link>

apps/sim/app/(landing)/components/hero/hero.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import dynamic from 'next/dynamic'
44
import Link from 'next/link'
55
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
6+
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
67

78
const LandingPreview = dynamic(
89
() =>
@@ -57,6 +58,7 @@ export default function Hero() {
5758
type='button'
5859
className={`${CTA_BASE} border-[var(--landing-border-strong)] bg-transparent text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]`}
5960
aria-label='Get a demo'
61+
onClick={() => trackLandingCta({ label: 'Get a demo', section: 'hero', destination: 'demo_modal' })}
6062
>
6163
Get a demo
6264
</button>
@@ -65,6 +67,7 @@ export default function Hero() {
6567
href='/signup'
6668
className={`${CTA_BASE} gap-2 border-white bg-white text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
6769
aria-label='Get started with Sim'
70+
onClick={() => trackLandingCta({ label: 'Get started', section: 'hero', destination: '/signup' })}
6871
>
6972
Get started
7073
</Link>

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { TypeBoolean, TypeNumber, TypeText } from '@/components/emcn/icons'
88
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
99
import { EASE_OUT } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
1010
import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder'
11+
import { captureClientEvent } from '@/lib/posthog/client'
1112

1213
const C = {
1314
SURFACE: '#292929',
@@ -151,6 +152,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
151152

152153
const handleSubmit = useCallback(() => {
153154
if (isEmpty) return
155+
captureClientEvent('landing_prompt_submitted', {})
154156
landingSubmit(inputValue)
155157
}, [isEmpty, inputValue, landingSubmit])
156158

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useRouter } from 'next/navigation'
88
import { createPortal } from 'react-dom'
99
import { Blimp, BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@/components/emcn'
1010
import { AgentIcon, HubspotIcon, OpenAIIcon, SalesforceIcon } from '@/components/icons'
11+
import { captureClientEvent } from '@/lib/posthog/client'
1112
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
1213
import {
1314
EASE_OUT,
@@ -147,6 +148,7 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel({
147148

148149
const handleSubmit = useCallback(() => {
149150
if (isEmpty) return
151+
captureClientEvent('landing_prompt_submitted', {})
150152
landingSubmit(inputValue)
151153
}, [isEmpty, inputValue, landingSubmit])
152154

apps/sim/app/(landing)/components/navbar/navbar.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useSearchParams } from 'next/navigation'
77
import { GithubOutlineIcon } from '@/components/icons'
88
import { useSession } from '@/lib/auth/auth-client'
99
import { cn } from '@/lib/core/utils/cn'
10+
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
1011
import {
1112
BlogDropdown,
1213
type NavBlogPost,
@@ -212,6 +213,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
212213
href='/workspace'
213214
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
214215
aria-label='Go to app'
216+
onClick={() => trackLandingCta({ label: 'Go to App', section: 'navbar', destination: '/workspace' })}
215217
>
216218
Go to App
217219
</Link>
@@ -221,13 +223,15 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
221223
href='/login'
222224
className='inline-flex h-[30px] items-center rounded-[5px] border border-[var(--landing-border-strong)] px-[9px] text-[13.5px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
223225
aria-label='Log in'
226+
onClick={() => trackLandingCta({ label: 'Log in', section: 'navbar', destination: '/login' })}
224227
>
225228
Log in
226229
</Link>
227230
<Link
228231
href='/signup'
229232
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-2.5 text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
230233
aria-label='Get started with Sim'
234+
onClick={() => trackLandingCta({ label: 'Get started', section: 'navbar', destination: '/signup' })}
231235
>
232236
Get started
233237
</Link>
@@ -303,7 +307,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
303307
<Link
304308
href='/workspace'
305309
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
306-
onClick={() => setMobileMenuOpen(false)}
310+
onClick={() => { trackLandingCta({ label: 'Go to App', section: 'navbar', destination: '/workspace' }); setMobileMenuOpen(false) }}
307311
aria-label='Go to app'
308312
>
309313
Go to App
@@ -313,15 +317,15 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
313317
<Link
314318
href='/login'
315319
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--landing-border-strong)] text-[14px] text-[var(--landing-text)] transition-colors active:bg-[var(--landing-bg-elevated)]'
316-
onClick={() => setMobileMenuOpen(false)}
320+
onClick={() => { trackLandingCta({ label: 'Log in', section: 'navbar', destination: '/login' }); setMobileMenuOpen(false) }}
317321
aria-label='Log in'
318322
>
319323
Log in
320324
</Link>
321325
<Link
322326
href='/signup'
323327
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
324-
onClick={() => setMobileMenuOpen(false)}
328+
onClick={() => { trackLandingCta({ label: 'Get started', section: 'navbar', destination: '/signup' }); setMobileMenuOpen(false) }}
325329
aria-label='Get started with Sim'
326330
>
327331
Get started

apps/sim/app/(landing)/components/pricing/pricing.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Link from 'next/link'
44
import { Badge } from '@/components/emcn'
55
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
6+
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
67

78
interface PricingTier {
89
id: string
@@ -150,6 +151,7 @@ function PricingCard({ tier }: PricingCardProps) {
150151
<button
151152
type='button'
152153
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[var(--landing-border-light)] bg-transparent px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:bg-[var(--landing-bg-hover)]'
154+
onClick={() => trackLandingCta({ label: tier.cta.label, section: 'pricing', destination: 'demo_modal' })}
153155
>
154156
{tier.cta.label}
155157
</button>
@@ -158,13 +160,15 @@ function PricingCard({ tier }: PricingCardProps) {
158160
<Link
159161
href={tier.cta.href || '/signup'}
160162
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-2.5 font-[430] font-season text-[14px] text-white transition-colors hover:border-[var(--landing-border)] hover:bg-[var(--landing-bg-elevated)]'
163+
onClick={() => trackLandingCta({ label: tier.cta.label, section: 'pricing', destination: tier.cta.href || '/signup' })}
161164
>
162165
{tier.cta.label}
163166
</Link>
164167
) : (
165168
<Link
166169
href={tier.cta.href || '/signup'}
167170
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[var(--landing-border-light)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:bg-[var(--landing-bg-hover)]'
171+
onClick={() => trackLandingCta({ label: tier.cta.label, section: 'pricing', destination: tier.cta.href || '/signup' })}
168172
>
169173
{tier.cta.label}
170174
</Link>

apps/sim/app/(landing)/landing-analytics.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import { useEffect } from 'react'
44
import { usePostHog } from 'posthog-js/react'
5-
import { captureEvent } from '@/lib/posthog/client'
5+
import { captureClientEvent, captureEvent } from '@/lib/posthog/client'
6+
import type { PostHogEventMap } from '@/lib/posthog/events'
67

78
export function LandingAnalytics() {
89
const posthog = usePostHog()
@@ -13,3 +14,13 @@ export function LandingAnalytics() {
1314

1415
return null
1516
}
17+
18+
/**
19+
* Fire-and-forget tracker for landing page CTA clicks.
20+
* Uses the non-hook client so it works in any click handler without requiring a PostHog provider ref.
21+
*/
22+
export function trackLandingCta(
23+
props: PostHogEventMap['landing_cta_clicked']
24+
): void {
25+
captureClientEvent('landing_cta_clicked', props)
26+
}

apps/sim/lib/posthog/events.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export interface PostHogEventMap {
1414

1515
landing_page_viewed: Record<string, never>
1616

17+
landing_cta_clicked: {
18+
label: string
19+
section: 'hero' | 'navbar' | 'footer_cta' | 'pricing'
20+
destination: string
21+
}
22+
23+
landing_demo_request_submitted: {
24+
company_size: string
25+
}
26+
27+
landing_prompt_submitted: Record<string, never>
28+
1729
signup_page_viewed: Record<string, never>
1830

1931
subscription_created: {

0 commit comments

Comments
 (0)