📈 Use posthog on landing page (#1817)

This commit is contained in:
Luke Vella 2025-07-13 11:48:53 +01:00 committed by GitHub
parent bfa3b0818e
commit dc40299896
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 109 additions and 21 deletions

View file

@ -15,6 +15,7 @@
"@rallly/billing": "workspace:*",
"@rallly/database": "workspace:*",
"@rallly/languages": "workspace:*",
"@rallly/posthog": "workspace:*",
"@rallly/tailwind-config": "workspace:*",
"@rallly/ui": "workspace:*",
"@rallly/utils": "workspace:*",

View file

@ -18,6 +18,8 @@ import { Trans } from "react-i18next/TransWithoutContext";
import { getTranslation } from "@/i18n/server";
import { linkToApp } from "@/lib/linkToApp";
import { LoginButton } from "@/components/login-button";
import { SignUpButton } from "@/components/sign-up-button";
import { Footer } from "./footer";
import { NavLink } from "./nav-link";
@ -65,16 +67,8 @@ export default async function Root(props: {
</div>
<div className="flex items-center gap-4 sm:gap-8">
<div className="hidden items-center gap-2 sm:flex">
<Button variant="ghost" asChild>
<Link href={linkToApp("/login")}>
<Trans t={t} i18nKey="login" defaults="Login" />
</Link>
</Button>
<Button asChild variant="primary">
<Link href={linkToApp("/register")}>
<Trans t={t} i18nKey="signUp" defaults="Sign up" />
</Link>
</Button>
<LoginButton />
<SignUpButton />
</div>
<div className="flex items-center justify-center lg:hidden">
<DropdownMenu>

View file

@ -5,8 +5,10 @@ import { Analytics } from "@vercel/analytics/react";
import { LazyMotion, domAnimation } from "motion/react";
import type { Viewport } from "next";
import { PostHogPageView } from "@/components/posthog-page-view";
import { sans } from "@/fonts/sans";
import { I18nProvider } from "@/i18n/client/i18n-provider";
import { PostHogProvider } from "@rallly/posthog/client";
export async function generateStaticParams() {
return Object.keys(languages).map((locale) => ({ locale }));
@ -31,7 +33,12 @@ export default async function Root(props: {
<html lang={locale} className={sans.className}>
<body>
<LazyMotion features={domAnimation}>
<I18nProvider locale={locale}>{children}</I18nProvider>
<I18nProvider locale={locale}>
<PostHogProvider>
<PostHogPageView />
{children}
</PostHogProvider>
</I18nProvider>
</LazyMotion>
<Analytics />
</body>

View file

@ -11,6 +11,7 @@ import * as React from "react";
import { handwritten } from "@/fonts/handwritten";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
const Screenshot = () => {
const [isLoaded, setIsLoaded] = React.useState(false);
@ -81,6 +82,7 @@ export const MarketingHero = ({
description: string;
callToAction: React.ReactNode;
}) => {
const posthog = usePostHog();
return (
<article className="max-w-full space-y-12 text-center">
<header className="sm:p-8">
@ -116,6 +118,9 @@ export const MarketingHero = ({
size="lg"
className="transition-all hover:shadow-md active:translate-y-1 active:shadow-none"
variant="primary"
onClick={() => {
posthog.capture("click_hero_button");
}}
asChild
>
<Link href={linkToApp("/new")}>{callToAction}</Link>

View file

@ -0,0 +1,24 @@
"use client";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import Link from "next/link";
export const LoginButton = () => {
const posthog = usePostHog();
return (
<Button
variant="ghost"
onClick={() => {
posthog.capture("click_login_button");
}}
asChild
>
<Link href={linkToApp("/login")}>
<Trans i18nKey="login" defaults="Login" />
</Link>
</Button>
);
};

View file

@ -0,0 +1,24 @@
"use client";
import { usePostHog } from "@rallly/posthog/client";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
export function PostHogPageView() {
const pathname = usePathname();
const searchParams = useSearchParams();
const posthog = usePostHog();
useEffect(() => {
// Track pageviews
if (pathname && posthog) {
let url = window.origin + pathname;
if (searchParams?.toString()) {
url = `${url}?${searchParams.toString()}`;
}
posthog.capture("$pageview", {
$current_url: url,
});
}
}, [pathname, searchParams, posthog]);
return null;
}

View file

@ -0,0 +1,24 @@
"use client";
import { Trans } from "@/i18n/client/trans";
import { linkToApp } from "@/lib/linkToApp";
import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import Link from "next/link";
export function SignUpButton() {
const posthog = usePostHog();
return (
<Button
variant="primary"
onClick={() => {
posthog.capture("click_sign_up_button");
}}
asChild
>
<Link href={linkToApp("/register")}>
<Trans i18nKey="signUp" defaults="Sign up" />
</Link>
</Button>
);
}

View file

@ -1,7 +1,7 @@
import "../../style.css";
import { supportedLngs } from "@rallly/languages";
import { PostHogProvider, posthog } from "@rallly/posthog/client";
import { PostHogProvider } from "@rallly/posthog/client";
import { Toaster } from "@rallly/ui/sonner";
import { TooltipProvider } from "@rallly/ui/tooltip";
import { LazyMotion, domAnimation } from "motion/react";
@ -74,7 +74,7 @@ export default async function Root({
<I18nProvider locale={locale}>
<TRPCProvider>
<LazyMotion features={domAnimation}>
<PostHogProvider client={posthog}>
<PostHogProvider>
<PostHogPageView />
<TooltipProvider>
<UserProvider

View file

@ -3,8 +3,8 @@
"version": "0.0.0",
"private": true,
"exports": {
"./server": "./src/server/index.ts",
"./client": "./src/client.ts",
"./server": "./src/server.ts",
"./client": "./src/client.tsx",
"./utils": "./src/utils.ts"
},
"scripts": {

View file

@ -4,7 +4,8 @@ import posthog from "posthog-js";
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "./constants";
export { PostHogProvider, usePostHog } from "posthog-js/react";
export { usePostHog } from "posthog-js/react";
import { PostHogProvider as BasePostHogProvider } from "posthog-js/react";
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
let bootstrapData = {};
@ -24,11 +25,16 @@ if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
capture_pageleave: true,
disable_session_recording: true,
enable_heatmaps: false,
persistence: "memory",
persistence: "localStorage",
bootstrap: bootstrapData,
autocapture: false,
opt_out_capturing_by_default: false,
cross_subdomain_cookie: true,
});
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
return <BasePostHogProvider client={posthog}>{children}</BasePostHogProvider>;
}
export { posthog };

11
pnpm-lock.yaml generated
View file

@ -63,6 +63,9 @@ importers:
'@rallly/languages':
specifier: workspace:*
version: link:../../packages/languages
'@rallly/posthog':
specifier: workspace:*
version: link:../../packages/posthog
'@rallly/tailwind-config':
specifier: workspace:*
version: link:../../packages/tailwind-config
@ -77,7 +80,7 @@ importers:
version: 8.1.0(typescript@5.8.3)
'@vercel/analytics':
specifier: ^1.5.0
version: 1.5.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
version: 1.5.0(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
dayjs:
specifier: ^1.11.13
version: 1.11.13
@ -116,7 +119,7 @@ importers:
version: 5.0.0(@types/react@19.1.2)(acorn@8.14.1)(react@19.1.0)
next-seo:
specifier: ^6.1.0
version: 6.6.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
version: 6.6.0(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react:
specifier: ^19.1.0
version: 19.1.0
@ -13635,7 +13638,7 @@ snapshots:
dependencies:
crypto-js: 4.2.0
'@vercel/analytics@1.5.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
'@vercel/analytics@1.5.0(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
optionalDependencies:
next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
@ -16774,7 +16777,7 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
next-seo@6.6.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
next-seo@6.6.0(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0