diff --git a/apps/sim/app/(landing)/components/demo-request/demo-request-modal.tsx b/apps/sim/app/(landing)/components/demo-request/demo-request-modal.tsx
index 01067670a1d..3ed6c53bf67 100644
--- a/apps/sim/app/(landing)/components/demo-request/demo-request-modal.tsx
+++ b/apps/sim/app/(landing)/components/demo-request/demo-request-modal.tsx
@@ -13,6 +13,7 @@ import {
Textarea,
} from '@/components/emcn'
import { Check } from '@/components/emcn/icons'
+import { captureClientEvent } from '@/lib/posthog/client'
import {
DEMO_REQUEST_COMPANY_SIZE_OPTIONS,
type DemoRequestPayload,
@@ -163,6 +164,9 @@ export function DemoRequestModal({ children, theme = 'dark' }: DemoRequestModalP
}
setSubmitSuccess(true)
+ captureClientEvent('landing_demo_request_submitted', {
+ company_size: parsed.data.companySize,
+ })
} catch (error) {
setSubmitError(
error instanceof Error
diff --git a/apps/sim/app/(landing)/components/footer/footer-cta.tsx b/apps/sim/app/(landing)/components/footer/footer-cta.tsx
index c1c95a638da..f9af4ac4bcc 100644
--- a/apps/sim/app/(landing)/components/footer/footer-cta.tsx
+++ b/apps/sim/app/(landing)/components/footer/footer-cta.tsx
@@ -3,7 +3,9 @@
import { useCallback, useRef, useState } from 'react'
import { ArrowUp } from 'lucide-react'
import Link from 'next/link'
+import { captureClientEvent } from '@/lib/posthog/client'
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
+import { trackLandingCta } from '@/app/(landing)/landing-analytics'
import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder'
const MAX_HEIGHT = 120
@@ -21,6 +23,7 @@ export function FooterCTA() {
const handleSubmit = useCallback(() => {
if (isEmpty) return
+ captureClientEvent('landing_prompt_submitted', {})
landingSubmit(inputValue)
}, [isEmpty, inputValue, landingSubmit])
@@ -94,12 +97,22 @@ export function FooterCTA() {
target='_blank'
rel='noopener noreferrer'
className={`${CTA_BUTTON} border-[var(--landing-border-strong)] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]`}
+ onClick={() =>
+ trackLandingCta({
+ label: 'Docs',
+ section: 'footer_cta',
+ destination: 'https://docs.sim.ai',
+ })
+ }
>
Docs
+ trackLandingCta({ label: 'Get started', section: 'footer_cta', destination: '/signup' })
+ }
>
Get started
diff --git a/apps/sim/app/(landing)/components/hero/hero.tsx b/apps/sim/app/(landing)/components/hero/hero.tsx
index 775f241c337..7098c4abf71 100644
--- a/apps/sim/app/(landing)/components/hero/hero.tsx
+++ b/apps/sim/app/(landing)/components/hero/hero.tsx
@@ -3,6 +3,7 @@
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
+import { trackLandingCta } from '@/app/(landing)/landing-analytics'
const LandingPreview = dynamic(
() =>
@@ -57,6 +58,9 @@ export default function Hero() {
type='button'
className={`${CTA_BASE} border-[var(--landing-border-strong)] bg-transparent text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]`}
aria-label='Get a demo'
+ onClick={() =>
+ trackLandingCta({ label: 'Get a demo', section: 'hero', destination: 'demo_modal' })
+ }
>
Get a demo
@@ -65,6 +69,9 @@ export default function Hero() {
href='/signup'
className={`${CTA_BASE} gap-2 border-white bg-white text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
aria-label='Get started with Sim'
+ onClick={() =>
+ trackLandingCta({ label: 'Get started', section: 'hero', destination: '/signup' })
+ }
>
Get started
diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx
index 35cb85c1654..aea260a3cb0 100644
--- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx
+++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx
@@ -5,6 +5,7 @@ import { AnimatePresence, motion } from 'framer-motion'
import { ArrowUp, Table } from 'lucide-react'
import { Blimp, Checkbox, ChevronDown } from '@/components/emcn'
import { TypeBoolean, TypeNumber, TypeText } from '@/components/emcn/icons'
+import { captureClientEvent } from '@/lib/posthog/client'
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
import { EASE_OUT } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder'
@@ -151,6 +152,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
const handleSubmit = useCallback(() => {
if (isEmpty) return
+ captureClientEvent('landing_prompt_submitted', {})
landingSubmit(inputValue)
}, [isEmpty, inputValue, landingSubmit])
diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx
index 4ca06238463..ef5929963e7 100644
--- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx
+++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx
@@ -9,6 +9,7 @@ import { createPortal } from 'react-dom'
import { Blimp, BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@/components/emcn'
import { AgentIcon, HubspotIcon, OpenAIIcon, SalesforceIcon } from '@/components/icons'
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
+import { captureClientEvent } from '@/lib/posthog/client'
import {
EASE_OUT,
type EditorPromptData,
@@ -147,6 +148,7 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel({
const handleSubmit = useCallback(() => {
if (isEmpty) return
+ captureClientEvent('landing_prompt_submitted', {})
landingSubmit(inputValue)
}, [isEmpty, inputValue, landingSubmit])
diff --git a/apps/sim/app/(landing)/components/navbar/navbar.tsx b/apps/sim/app/(landing)/components/navbar/navbar.tsx
index 0cc2a8c306a..8f595d69078 100644
--- a/apps/sim/app/(landing)/components/navbar/navbar.tsx
+++ b/apps/sim/app/(landing)/components/navbar/navbar.tsx
@@ -13,6 +13,7 @@ import {
} from '@/app/(landing)/components/navbar/components/blog-dropdown'
import { DocsDropdown } from '@/app/(landing)/components/navbar/components/docs-dropdown'
import { GitHubStars } from '@/app/(landing)/components/navbar/components/github-stars'
+import { trackLandingCta } from '@/app/(landing)/landing-analytics'
import { getBrandConfig } from '@/ee/whitelabeling'
type DropdownId = 'docs' | 'blog' | null
@@ -212,6 +213,13 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
href='/workspace'
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]'
aria-label='Go to app'
+ onClick={() =>
+ trackLandingCta({
+ label: 'Go to App',
+ section: 'navbar',
+ destination: '/workspace',
+ })
+ }
>
Go to App
@@ -221,6 +229,9 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
href='/login'
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)]'
aria-label='Log in'
+ onClick={() =>
+ trackLandingCta({ label: 'Log in', section: 'navbar', destination: '/login' })
+ }
>
Log in
@@ -228,6 +239,13 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
href='/signup'
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]'
aria-label='Get started with Sim'
+ onClick={() =>
+ trackLandingCta({
+ label: 'Get started',
+ section: 'navbar',
+ destination: '/signup',
+ })
+ }
>
Get started
@@ -303,7 +321,14 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
setMobileMenuOpen(false)}
+ onClick={() => {
+ trackLandingCta({
+ label: 'Go to App',
+ section: 'navbar',
+ destination: '/workspace',
+ })
+ setMobileMenuOpen(false)
+ }}
aria-label='Go to app'
>
Go to App
@@ -313,7 +338,10 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
setMobileMenuOpen(false)}
+ onClick={() => {
+ trackLandingCta({ label: 'Log in', section: 'navbar', destination: '/login' })
+ setMobileMenuOpen(false)
+ }}
aria-label='Log in'
>
Log in
@@ -321,7 +349,14 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
setMobileMenuOpen(false)}
+ onClick={() => {
+ trackLandingCta({
+ label: 'Get started',
+ section: 'navbar',
+ destination: '/signup',
+ })
+ setMobileMenuOpen(false)
+ }}
aria-label='Get started with Sim'
>
Get started
diff --git a/apps/sim/app/(landing)/components/pricing/pricing.tsx b/apps/sim/app/(landing)/components/pricing/pricing.tsx
index 455aea124c8..d4d0789467c 100644
--- a/apps/sim/app/(landing)/components/pricing/pricing.tsx
+++ b/apps/sim/app/(landing)/components/pricing/pricing.tsx
@@ -3,6 +3,7 @@
import Link from 'next/link'
import { Badge } from '@/components/emcn'
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
+import { trackLandingCta } from '@/app/(landing)/landing-analytics'
interface PricingTier {
id: string
@@ -150,6 +151,13 @@ function PricingCard({ tier }: PricingCardProps) {
@@ -158,6 +166,13 @@ function PricingCard({ tier }: PricingCardProps) {
+ trackLandingCta({
+ label: tier.cta.label,
+ section: 'pricing',
+ destination: tier.cta.href || '/signup',
+ })
+ }
>
{tier.cta.label}
@@ -165,6 +180,13 @@ function PricingCard({ tier }: PricingCardProps) {
+ trackLandingCta({
+ label: tier.cta.label,
+ section: 'pricing',
+ destination: tier.cta.href || '/signup',
+ })
+ }
>
{tier.cta.label}
diff --git a/apps/sim/app/(landing)/landing-analytics.tsx b/apps/sim/app/(landing)/landing-analytics.tsx
index 10be29e5edd..d79e5faaa52 100644
--- a/apps/sim/app/(landing)/landing-analytics.tsx
+++ b/apps/sim/app/(landing)/landing-analytics.tsx
@@ -2,7 +2,8 @@
import { useEffect } from 'react'
import { usePostHog } from 'posthog-js/react'
-import { captureEvent } from '@/lib/posthog/client'
+import { captureClientEvent, captureEvent } from '@/lib/posthog/client'
+import type { PostHogEventMap } from '@/lib/posthog/events'
export function LandingAnalytics() {
const posthog = usePostHog()
@@ -13,3 +14,11 @@ export function LandingAnalytics() {
return null
}
+
+/**
+ * Fire-and-forget tracker for landing page CTA clicks.
+ * Uses the non-hook client so it works in any click handler without requiring a PostHog provider ref.
+ */
+export function trackLandingCta(props: PostHogEventMap['landing_cta_clicked']): void {
+ captureClientEvent('landing_cta_clicked', props)
+}
diff --git a/apps/sim/lib/posthog/events.ts b/apps/sim/lib/posthog/events.ts
index c186d82cd47..8d8a1d194b2 100644
--- a/apps/sim/lib/posthog/events.ts
+++ b/apps/sim/lib/posthog/events.ts
@@ -14,6 +14,18 @@ export interface PostHogEventMap {
landing_page_viewed: Record
+ landing_cta_clicked: {
+ label: string
+ section: 'hero' | 'navbar' | 'footer_cta' | 'pricing'
+ destination: string
+ }
+
+ landing_demo_request_submitted: {
+ company_size: string
+ }
+
+ landing_prompt_submitted: Record
+
signup_page_viewed: Record
subscription_created: {