Skip to content

Commit 30ba8e1

Browse files
feat: integrate PostHog for analytics tracking
1 parent e9c3502 commit 30ba8e1

6 files changed

Lines changed: 137 additions & 33 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
.env
23

34
# dependencies
45
/node_modules

next.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ module.exports = withTM({
1212
source: '/api/:path*',
1313
destination: 'https://dock2.csslab.ca/api/:path*',
1414
},
15+
{
16+
source: '/ingest/static/:path*',
17+
destination: 'https://us-assets.i.posthog.com/static/:path*',
18+
},
19+
{
20+
source: '/ingest/:path*',
21+
destination: 'https://us.i.posthog.com/:path*',
22+
},
23+
{
24+
source: '/ingest/decide',
25+
destination: 'https://us.i.posthog.com/decide',
26+
},
1527
]
1628
},
1729
async headers() {
@@ -31,4 +43,5 @@ module.exports = withTM({
3143
},
3244
]
3345
},
46+
skipTrailingSlashRedirect: true,
3447
})

package-lock.json

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"next": "15.1.6",
2727
"next-transpile-modules": "^10.0.1",
2828
"onnxruntime-web": "^1.21.0-dev.20250114-228dd16893",
29+
"posthog-js": "^1.257.0",
30+
"posthog-node": "^5.3.1",
2931
"react": "18.3.1",
3032
"react-chartjs-2": "^5.2.0",
3133
"react-dom": "18.3.1",

src/pages/_app.tsx

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import Head from 'next/head'
22
import { Toaster } from 'react-hot-toast'
33
import Script from 'next/script'
44
import { useRouter } from 'next/router'
5+
import { useEffect } from 'react'
56
import type { AppProps } from 'next/app'
67
import { Open_Sans } from 'next/font/google'
78
import { Analytics } from '@vercel/analytics/react'
9+
import posthog from 'posthog-js'
10+
import { PostHogProvider } from 'posthog-js/react'
811

912
import {
1013
AuthContextProvider,
@@ -28,47 +31,59 @@ function MaiaPlatform({ Component, pageProps }: AppProps) {
2831
const router = useRouter()
2932
const isAnalysisPage = router.pathname.startsWith('/analysis')
3033

34+
useEffect(() => {
35+
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
36+
api_host: '/ingest',
37+
ui_host: 'https://us.posthog.com',
38+
defaults: '2025-05-24',
39+
capture_exceptions: true,
40+
debug: process.env.NODE_ENV === 'development',
41+
})
42+
}, [])
43+
3144
return (
32-
<TourContextProvider>
33-
<TourManager />
34-
<Compose
35-
components={[
36-
ErrorBoundary,
37-
ThemeContextProvider,
38-
WindowSizeContextProvider,
39-
AuthContextProvider,
40-
ModalContextProvider,
41-
...(isAnalysisPage ? [AnalysisListContextProvider] : []),
42-
]}
43-
>
44-
<Head>
45-
<link rel="icon" type="image/png" href="/favicon.png" />
46-
<link
47-
rel="stylesheet"
48-
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=swap"
49-
/>
50-
</Head>
51-
<div className={`${OpenSans.className} app-container`}>
52-
<Header />
53-
<div className="content-container">
54-
<Component {...pageProps} />
45+
<PostHogProvider client={posthog}>
46+
<TourContextProvider>
47+
<TourManager />
48+
<Compose
49+
components={[
50+
ErrorBoundary,
51+
ThemeContextProvider,
52+
WindowSizeContextProvider,
53+
AuthContextProvider,
54+
ModalContextProvider,
55+
...(isAnalysisPage ? [AnalysisListContextProvider] : []),
56+
]}
57+
>
58+
<Head>
59+
<link rel="icon" type="image/png" href="/favicon.png" />
60+
<link
61+
rel="stylesheet"
62+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=swap"
63+
/>
64+
</Head>
65+
<div className={`${OpenSans.className} app-container`}>
66+
<Header />
67+
<div className="content-container">
68+
<Component {...pageProps} />
69+
</div>
70+
<Footer />
5571
</div>
56-
<Footer />
57-
</div>
58-
<Toaster position="bottom-right" />
59-
<Analytics />
60-
<Script async src="/analytics.js?id=G-SNP84LXLKY" />
61-
<Script id="analytics">
62-
{`
72+
<Toaster position="bottom-right" />
73+
<Analytics />
74+
<Script async src="/analytics.js?id=G-SNP84LXLKY" />
75+
<Script id="analytics">
76+
{`
6377
window.dataLayer = window.dataLayer || [];
6478
function gtag(){dataLayer.push(arguments);}
6579
gtag('js', new Date());
6680
6781
gtag('config', 'G-SNP84LXLKY');
6882
`}
69-
</Script>
70-
</Compose>
71-
</TourContextProvider>
83+
</Script>
84+
</Compose>
85+
</TourContextProvider>
86+
</PostHogProvider>
7287
)
7388
}
7489

src/utils/posthog.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { PostHog } from 'posthog-node'
2+
3+
// NOTE: This is a Node.js client for sending events from the server side to PostHog.
4+
export default function PostHogClient(): PostHog {
5+
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
6+
host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
7+
flushAt: 1,
8+
flushInterval: 0,
9+
})
10+
return posthogClient
11+
}

0 commit comments

Comments
 (0)