diff --git a/POSTHOG_SETUP.md b/POSTHOG_SETUP.md new file mode 100644 index 0000000..e8f213e --- /dev/null +++ b/POSTHOG_SETUP.md @@ -0,0 +1,95 @@ +# PostHog Analytics Setup Guide + +This guide will help you set up PostHog analytics for your MkSaaS application. + +## Quick Setup + +### 1. Get PostHog API Key + +1. Go to [PostHog](https://posthog.com) and create an account +2. Create a new project (or use an existing one) +3. Go to **Project Settings** → **API Keys** +4. Copy your **Project API Key** (starts with `phc_`) + +### 2. Configure Environment Variables + +Add the following variables to your `.env.local` file: + +```bash +# PostHog Analytics +NEXT_PUBLIC_POSTHOG_KEY=your_posthog_project_key_here +NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com +``` + +**Note:** The default host `https://us.i.posthog.com` works for most users. If you're using PostHog Cloud EU, use `https://eu.i.posthog.com` instead. + +### 3. Install Dependencies + +If PostHog isn't already installed, run: + +```bash +pnpm add posthog-js +``` + +### 4. Verify Setup + +1. Start your development server: `pnpm dev` +2. Open your browser and visit `http://localhost:3000` +3. Check the browser's developer console for any errors +4. In PostHog dashboard, go to **Live Events** to see real-time tracking + +## Available Environment Variables + +| Variable | Description | Example | +|----------|-------------|---------| +| `NEXT_PUBLIC_POSTHOG_KEY` | Your PostHog project API key | `phc_abc123...` | +| `NEXT_PUBLIC_POSTHOG_HOST` | PostHog instance URL | `https://us.i.posthog.com` | + +## Features Included + +The integration includes: + +- **Page view tracking** - Automatic tracking of all page views +- **Custom event capture** - Ready to use with your own events +- **User identification** - Links events to logged-in users +- **Cross-domain tracking** - Works with your domain setup +- **Performance tracking** - Monitors page load times + +## Custom Events Usage + +You can track custom events in your components: + +```typescript +import { usePostHog } from 'posthog-js/react'; + +function MyComponent() { + const posthog = usePostHog(); + + const handleClick = () => { + posthog.capture('button_clicked', { + button_name: 'upgrade_plan', + user_tier: 'free' + }); + }; + + return ; +} +``` + +## Troubleshooting + +### Events not appearing in PostHog? +1. Check that `NEXT_PUBLIC_POSTHOG_KEY` is set correctly +2. Verify the API key starts with `phc_` +3. Ensure you're in production mode or events won't be sent in development +4. Check the browser console for any JavaScript errors + +### Development vs Production +- **Development**: Events are only sent if you manually enable them +- **Production**: Events are automatically sent when environment variables are configured + +### Next Steps +- Explore [PostHog documentation](https://posthog.com/docs) for advanced features +- Set up user properties and cohorts +- Create custom dashboards and insights +- Consider setting up feature flags for A/B testing \ No newline at end of file diff --git a/env.example b/env.example index 81edc13..45bb260 100644 --- a/env.example +++ b/env.example @@ -129,6 +129,12 @@ NEXT_PUBLIC_SELINE_TOKEN="" # ----------------------------------------------------------------------------- NEXT_PUBLIC_DATAFAST_WEBSITE_ID="" NEXT_PUBLIC_DATAFAST_DOMAIN="" +# ----------------------------------------------------------------------------- +# PostHog Analytics (https://posthog.com) +# https://mksaas.com/docs/analytics#posthog +# ----------------------------------------------------------------------------- +NEXT_PUBLIC_POSTHOG_KEY="" +NEXT_PUBLIC_POSTHOG_HOST="https://us.i.posthog.com" # ----------------------------------------------------------------------------- diff --git a/src/analytics/analytics.tsx b/src/analytics/analytics.tsx index 5ad2455..ac56e96 100644 --- a/src/analytics/analytics.tsx +++ b/src/analytics/analytics.tsx @@ -6,6 +6,7 @@ import DataFastAnalytics from './data-fast-analytics'; import GoogleAnalytics from './google-analytics'; import OpenPanelAnalytics from './open-panel-analytics'; import { PlausibleAnalytics } from './plausible-analytics'; +import PostHogAnalytics from './posthog-analytics'; import { SelineAnalytics } from './seline-analytics'; import { UmamiAnalytics } from './umami-analytics'; @@ -46,6 +47,9 @@ export function Analytics() { {/* seline analytics */} + {/* posthog analytics */} + + {/* vercel analytics */} {/* https://vercel.com/docs/analytics/quickstart */} {websiteConfig.analytics.enableVercelAnalytics && } diff --git a/src/analytics/posthog-analytics.tsx b/src/analytics/posthog-analytics.tsx new file mode 100644 index 0000000..9f1c696 --- /dev/null +++ b/src/analytics/posthog-analytics.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { usePathname, useSearchParams } from 'next/navigation'; +import { usePostHog } from 'posthog-js/react'; +import { useEffect, Suspense } from 'react'; + +function PostHogPageView() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const posthog = usePostHog(); + + useEffect(() => { + 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; +} + +export default function PostHogAnalytics() { + const key = process.env.NEXT_PUBLIC_POSTHOG_KEY; + const host = process.env.NEXT_PUBLIC_POSTHOG_HOST; + + if (!key || !host) { + return null; + } + + return ( + + + + ); +} \ No newline at end of file diff --git a/src/app/[locale]/posthog-provider.tsx b/src/app/[locale]/posthog-provider.tsx new file mode 100644 index 0000000..fa4e593 --- /dev/null +++ b/src/app/[locale]/posthog-provider.tsx @@ -0,0 +1,34 @@ +'use client'; + +import posthog from 'posthog-js'; +import { PostHogProvider } from 'posthog-js/react'; +import type { ReactNode } from 'react'; + +interface PostHogClientProviderProps { + children: ReactNode; +} + +export default function PostHogClientProvider({ + children, +}: PostHogClientProviderProps) { + const key = process.env.NEXT_PUBLIC_POSTHOG_KEY; + const host = process.env.NEXT_PUBLIC_POSTHOG_HOST; + + useEffect(() => { + if (key && host && typeof window !== 'undefined' && !posthog.__loaded) { + posthog.init(key, { + api_host: host, + person_profiles: 'identified_only', + capture_pageview: false, // Disable automatic pageview capture, as we capture manually + capture_pageleave: true, + }); + } + }, [key, host]); + + if (!key || !host) { + return <>{children}; + } + + return {children}; +} + diff --git a/src/app/[locale]/providers.tsx b/src/app/[locale]/providers.tsx index 5e85838..d814d69 100644 --- a/src/app/[locale]/providers.tsx +++ b/src/app/[locale]/providers.tsx @@ -9,8 +9,11 @@ import type { Translations } from 'fumadocs-ui/i18n'; import { RootProvider } from 'fumadocs-ui/provider'; import { useTranslations } from 'next-intl'; import { ThemeProvider, useTheme } from 'next-themes'; +import dynamic from 'next/dynamic'; import type { ReactNode } from 'react'; +const PostHogProvider = dynamic(() => import('./posthog-provider'), { ssr: false }); + interface ProvidersProps { children: ReactNode; locale: string; @@ -62,11 +65,13 @@ export function Providers({ children, locale }: ProvidersProps) { > - - - {children} - - + + + + {children} + + +