diff --git a/frontend/instrumentation-client.ts b/frontend/instrumentation-client.ts
new file mode 100644
index 00000000..9d72b8a8
--- /dev/null
+++ b/frontend/instrumentation-client.ts
@@ -0,0 +1,11 @@
+import posthog from 'posthog-js';
+
+if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
+ posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
+ api_host: '/ingest',
+ ui_host: 'https://eu.posthog.com',
+ defaults: '2025-05-24',
+ capture_exceptions: true, // This enables capturing exceptions using Error Tracking, set to false if you don't want this
+ debug: process.env.NODE_ENV === 'development',
+ });
+}
diff --git a/frontend/next.config.ts b/frontend/next.config.ts
index efd187d5..eb96f75d 100644
--- a/frontend/next.config.ts
+++ b/frontend/next.config.ts
@@ -2,6 +2,23 @@ import type { NextConfig } from 'next';
const nextConfig = (): NextConfig => ({
output: (process.env.NEXT_OUTPUT as 'standalone') || undefined,
+ async rewrites() {
+ return [
+ {
+ source: '/ingest/static/:path*',
+ destination: 'https://eu-assets.i.posthog.com/static/:path*',
+ },
+ {
+ source: '/ingest/:path*',
+ destination: 'https://eu.i.posthog.com/:path*',
+ },
+ {
+ source: '/ingest/flags',
+ destination: 'https://eu.i.posthog.com/flags',
+ },
+ ];
+ },
+ skipTrailingSlashRedirect: true,
});
export default nextConfig;
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 3af55459..238ee6ee 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -86,6 +86,8 @@
"papaparse": "^5.5.2",
"pdfjs-dist": "4.8.69",
"postcss": "8.4.33",
+ "posthog-js": "^1.258.6",
+ "posthog-node": "^5.6.0",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-diff-viewer-continued": "^3.4.0",
@@ -7473,7 +7475,6 @@
"integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==",
"hasInstallScript": true,
"license": "MIT",
- "optional": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -12488,6 +12489,45 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
+ "node_modules/posthog-js": {
+ "version": "1.258.6",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.258.6.tgz",
+ "integrity": "sha512-vL5AGG+rOoRg3LGquMfBPO55jD4bGl0CiV44SHdHAoBnOVDDAqxczRGDqMdxor+VLx3/ofTFOJ2FNprfAHp70Q==",
+ "license": "SEE LICENSE IN LICENSE",
+ "dependencies": {
+ "core-js": "^3.38.1",
+ "fflate": "^0.4.8",
+ "preact": "^10.19.3",
+ "web-vitals": "^4.2.4"
+ },
+ "peerDependencies": {
+ "@rrweb/types": "2.0.0-alpha.17",
+ "rrweb-snapshot": "2.0.0-alpha.17"
+ },
+ "peerDependenciesMeta": {
+ "@rrweb/types": {
+ "optional": true
+ },
+ "rrweb-snapshot": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/posthog-js/node_modules/fflate": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+ "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
+ "license": "MIT"
+ },
+ "node_modules/posthog-node": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.6.0.tgz",
+ "integrity": "sha512-MVXxKmqAYp2cPBrN1YMhnhYsJYIu6yc6wumbHz1dbo67wZBf2WtMm67Uh+4VCrp07049qierWlxQqz1W5zGDeg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/preact": {
"version": "10.26.8",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz",
@@ -14915,6 +14955,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/web-vitals": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
+ "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
+ "license": "Apache-2.0"
+ },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 88a12db3..a8ab89e9 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -89,6 +89,8 @@
"papaparse": "^5.5.2",
"pdfjs-dist": "4.8.69",
"postcss": "8.4.33",
+ "posthog-js": "^1.258.6",
+ "posthog-node": "^5.6.0",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-diff-viewer-continued": "^3.4.0",
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
index 16734734..084bece1 100644
--- a/frontend/src/app/layout.tsx
+++ b/frontend/src/app/layout.tsx
@@ -9,6 +9,7 @@ import { Analytics } from '@vercel/analytics/react';
import { GoogleAnalytics } from '@next/third-parties/google';
import { SpeedInsights } from '@vercel/speed-insights/next';
import Script from 'next/script';
+import { PostHogIdentify } from '@/components/posthog-identify';
const geistSans = Geist({
variable: '--font-geist-sans',
@@ -152,6 +153,7 @@ export default function RootLayout({
+