feat: support posthog analytics

This commit is contained in:
javayhu 2025-08-06 00:40:23 +08:00
parent 2b72570784
commit ada95848f9
6 changed files with 189 additions and 5 deletions

95
POSTHOG_SETUP.md Normal file
View File

@ -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 <button onClick={handleClick}>Upgrade</button>;
}
```
## 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

View File

@ -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"
# -----------------------------------------------------------------------------

View File

@ -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 */}
<SelineAnalytics />
{/* posthog analytics */}
<PostHogAnalytics />
{/* vercel analytics */}
{/* https://vercel.com/docs/analytics/quickstart */}
{websiteConfig.analytics.enableVercelAnalytics && <VercelAnalytics />}

View File

@ -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 (
<Suspense fallback={null}>
<PostHogPageView />
</Suspense>
);
}

View File

@ -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 <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

View File

@ -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) {
>
<ActiveThemeProvider>
<RootProvider theme={theme} i18n={{ locale, locales, translations }}>
<TooltipProvider>
<PaymentProvider>
<CreditsProvider>{children}</CreditsProvider>
</PaymentProvider>
</TooltipProvider>
<PostHogProvider>
<TooltipProvider>
<PaymentProvider>
<CreditsProvider>{children}</CreditsProvider>
</PaymentProvider>
</TooltipProvider>
</PostHogProvider>
</RootProvider>
</ActiveThemeProvider>
</ThemeProvider>