diff --git a/app/providers.tsx b/app/providers.tsx index 3e494d7..253ff45 100644 --- a/app/providers.tsx +++ b/app/providers.tsx @@ -2,6 +2,8 @@ import PlausibleProvider from 'next-plausible'; import { ThemeProvider } from 'next-themes'; +import posthog from 'posthog-js'; +import { PostHogProvider } from 'posthog-js/react'; import { type FC, type ReactNode } from 'react'; import { SWRConfig } from 'swr'; @@ -30,6 +32,14 @@ const CustomizedPlausibleProvider: FC = ({ ); }; +if (typeof window !== 'undefined' && env.NEXT_PUBLIC_POSTHOG_KEY) { + posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, { + api_host: '/ingest', + ui_host: 'https://eu.posthog.com', + person_profiles: 'always', + }); +} + const swrFetcher = async (url: string) => { const response = await fetch(url); if (!response.ok) throw new Error(await response.text()); @@ -43,7 +53,9 @@ type ProvidersProps = { export const Providers: FC = ({ children }) => ( - {children} + + {children} + ); diff --git a/env.ts b/env.ts index b9f5626..f1959e4 100644 --- a/env.ts +++ b/env.ts @@ -53,10 +53,12 @@ export const env = createEnv({ client: { NEXT_PUBLIC_PLAUSIBLE_DOMAIN: z.string().optional(), NEXT_PUBLIC_PLAUSIBLE_HOST: z.string().optional(), + NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(), }, experimental__runtimeEnv: { NEXT_PUBLIC_PLAUSIBLE_DOMAIN: process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN, NEXT_PUBLIC_PLAUSIBLE_HOST: process.env.NEXT_PUBLIC_PLAUSIBLE_HOST, + NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, }, emptyStringAsUndefined: true, }); diff --git a/lib/analytics.ts b/lib/analytics.ts index f0439fa..c58b503 100644 --- a/lib/analytics.ts +++ b/lib/analytics.ts @@ -1,4 +1,5 @@ import { usePlausible } from 'next-plausible'; +import posthog from 'posthog-js'; import { useCallback, useMemo } from 'react'; type EmptyEvent = Record; @@ -27,6 +28,7 @@ export const useAnalytics = () => { const reportEvent = useCallback( (event: T, props: Events[T]) => { plausible(event, { props }); + posthog.capture(event, props); }, [plausible], ); diff --git a/next.config.js b/next.config.js index fe22edc..9ecb861 100644 --- a/next.config.js +++ b/next.config.js @@ -44,6 +44,20 @@ const nextConfig = { ], formats: ['image/avif', 'image/webp'], }, + rewrites: async () => [ + { + 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/decide', + destination: 'https://eu.i.posthog.com/decide', + }, + ], redirects: async () => [ { source: '/lookup', diff --git a/package.json b/package.json index 9e75d23..1798f12 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "next-plausible": "^3.12.4", "next-themes": "^0.4.3", "postcss": "^8.4.49", + "posthog-js": "^1.202.2", "punycode": "^2.3.1", "react": "19.0.0", "react-dom": "19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66cbdb0..ec056d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,6 +101,9 @@ importers: postcss: specifier: ^8.4.49 version: 8.4.49 + posthog-js: + specifier: ^1.202.2 + version: 1.202.2 punycode: specifier: ^2.3.1 version: 2.3.1 @@ -2254,6 +2257,9 @@ packages: core-js-compat@3.39.0: resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} + core-js@3.39.0: + resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -2659,6 +2665,9 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -3356,6 +3365,12 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + posthog-js@1.202.2: + resolution: {integrity: sha512-9p7dAWuCfoM0WrasubGwtC8i38HU3iMqK3gd0mhyAoTrEVMVozTQq64Toc2VEv8H69NGNn6ikk5t2LclHT9XFA==} + + preact@10.25.2: + resolution: {integrity: sha512-GEts1EH3oMnqdOIeXhlbBSddZ9nrINd070WBOiPO2ous1orrKGUM4SMDbwyjSWD1iMS2dBvaDjAa5qUhz3TXqw==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4013,6 +4028,9 @@ packages: web-auth-library@1.0.3: resolution: {integrity: sha512-fBmEjJSrbmbD9EREwVlewyfSVkb3IzgTXEF0fzXo3miDywsxES1vwG4aJGNpuSSUorZAGBJNyyz5VFq2VFgudw==} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -6211,6 +6229,8 @@ snapshots: dependencies: browserslist: 4.24.2 + core-js@3.39.0: {} + cosmiconfig@8.3.6(typescript@5.6.3): dependencies: import-fresh: 3.3.0 @@ -6792,6 +6812,8 @@ snapshots: dependencies: reusify: 1.0.4 + fflate@0.4.8: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -7440,6 +7462,15 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + posthog-js@1.202.2: + dependencies: + core-js: 3.39.0 + fflate: 0.4.8 + preact: 10.25.2 + web-vitals: 4.2.4 + + preact@10.25.2: {} + prelude-ls@1.2.1: {} prettier-plugin-tailwindcss@0.6.8(@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.3.3))(prettier@3.3.3): @@ -8147,6 +8178,8 @@ snapshots: jose: 4.15.9 rfc4648: 1.5.3 + web-vitals@4.2.4: {} + webidl-conversions@7.0.0: {} whatwg-mimetype@3.0.0: {}