Skip to content

Commit 17e03c1

Browse files
committed
feat: enable posthog
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
1 parent 4219484 commit 17e03c1

File tree

8 files changed

+109
-10
lines changed

8 files changed

+109
-10
lines changed

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
}
2929
},
3030
"include": ["./src/*"],
31-
"ignore": ["./src/components/ui/*"]
31+
"ignore": ["./src/components/ui/*", "src/app/providers.tsx"]
3232
},
3333
"formatter": {
3434
"enabled": true,

bun.lockb

1.69 KB
Binary file not shown.

example.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Base URL
2+
NEXT_PUBLIC_BASE_URL="localhost:3000"
3+
4+
# PostHog
5+
NEXT_PUBLIC_POSTHOG_KEY=""
6+
NEXT_PUBLIC_POSTHOG_HOST=""

next.config.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4-
/* config options here */
54
images: {
65
remotePatterns: [
76
{
@@ -12,6 +11,36 @@ const nextConfig: NextConfig = {
1211
},
1312
],
1413
},
14+
async rewrites() {
15+
const postHogBaseURL = process.env.NEXT_PUBLIC_POSTHOG_HOST;
16+
if (!postHogBaseURL) {
17+
return [];
18+
}
19+
20+
const assetPath =
21+
postHogBaseURL === "https://us.i.posthog.com"
22+
? "https://us-assets.i.posthog.com"
23+
: postHogBaseURL === "https://eu.i.posthog.com"
24+
? "https://eu-assets.i.posthog.com"
25+
: process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST || "";
26+
27+
return [
28+
{
29+
source: "/ingest/static/:path*",
30+
destination: assetPath + "/static/:path*",
31+
},
32+
{
33+
source: "/ingest/:path*",
34+
destination: postHogBaseURL + "/:path*",
35+
},
36+
{
37+
source: "/ingest/decide",
38+
destination: postHogBaseURL + "/decide",
39+
},
40+
];
41+
},
42+
// This is required to support PostHog trailing slash API requests
43+
skipTrailingSlashRedirect: true,
1544
};
1645

1746
export default nextConfig;

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,18 @@
1212
},
1313
"dependencies": {
1414
"@radix-ui/react-accordion": "^1.2.2",
15-
"@radix-ui/react-dialog": "^1.1.4",
16-
"@radix-ui/react-dropdown-menu": "^2.1.4",
17-
"@radix-ui/react-navigation-menu": "^1.2.3",
15+
"@radix-ui/react-dialog": "^1.1.5",
16+
"@radix-ui/react-dropdown-menu": "^2.1.5",
17+
"@radix-ui/react-navigation-menu": "^1.2.4",
1818
"@radix-ui/react-slot": "^1.1.1",
1919
"class-variance-authority": "^0.7.1",
2020
"clsx": "^2.1.1",
21-
"framer-motion": "^11.17.0",
22-
"lucide-react": "^0.469.0",
23-
"motion": "^11.17.0",
24-
"next": "15.1.4",
21+
"framer-motion": "^12.0.6",
22+
"lucide-react": "^0.474.0",
23+
"motion": "^12.0.6",
24+
"next": "15.1.6",
2525
"next-mdx-remote": "^5.0.0",
26+
"posthog-js": "^1.211.0",
2627
"react": "19.0.0",
2728
"react-dom": "19.0.0",
2829
"tailwind-merge": "^2.6.0",

src/app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Roboto_Mono } from "next/font/google";
33
import "./globals.css";
44
import NavBar from "@/components/nav-bar";
55
import Footer from "@/components/footer";
6+
import { PostHogProvider } from "@/app/providers";
67

78
const robotoMono = Roboto_Mono({
89
weight: "400",
@@ -48,7 +49,7 @@ export default function RootLayout({
4849
<body className={`${robotoMono.className} antialiased`}>
4950
<div className="absolute inset-0 -z-10 h-full w-full bg-white bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:14px_24px]">
5051
<NavBar />
51-
{children}
52+
<PostHogProvider>{children}</PostHogProvider>
5253
<Footer />
5354
</div>
5455
</body>

src/app/posthog-page-view.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import { usePathname, useSearchParams } from "next/navigation";
4+
import { useEffect, Suspense } from "react";
5+
import { usePostHog } from "posthog-js/react";
6+
7+
function PostHogPageView(): null {
8+
const pathname = usePathname();
9+
const searchParams = useSearchParams();
10+
const posthog = usePostHog();
11+
12+
// Track pageviews
13+
useEffect(() => {
14+
if (pathname && posthog) {
15+
let url = window.origin + pathname;
16+
if (searchParams.toString()) {
17+
url = `${url}?${searchParams.toString()}`;
18+
}
19+
20+
// biome-ignore lint/style/useNamingConvention: This is used by PostHog
21+
posthog.capture("$pageview", { $current_url: url });
22+
}
23+
}, [pathname, searchParams, posthog]);
24+
25+
return null;
26+
}
27+
28+
// Wrap this in Suspense to avoid the `useSearchParams` usage above
29+
// from de-opting the whole app into client-side rendering
30+
// See: https://nextjs.org/docs/messages/deopted-into-client-rendering
31+
export default function SuspendedPostHogPageView() {
32+
return (
33+
<Suspense fallback={null}>
34+
<PostHogPageView />
35+
</Suspense>
36+
);
37+
}

src/app/providers.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use client";
2+
3+
import posthog from "posthog-js";
4+
import { PostHogProvider as PHProvider } from "posthog-js/react";
5+
import { useEffect } from "react";
6+
import SuspendedPostHogPageView from "@/app/posthog-page-view";
7+
import type React from "react";
8+
9+
export function PostHogProvider({ children }: { children: React.ReactNode }) {
10+
useEffect(() => {
11+
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY as string, {
12+
api_host: "/ingest",
13+
ui_host: process.env.NEXT_PUBLIC_POSTHOG_HOST as string,
14+
capture_pageview: false,
15+
capture_pageleave: true,
16+
});
17+
}, []);
18+
19+
return (
20+
<PHProvider client={posthog}>
21+
<SuspendedPostHogPageView />
22+
{children}
23+
</PHProvider>
24+
);
25+
}

0 commit comments

Comments
 (0)