posthog-analytics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PostHog Analytics Skill

PostHog 分析技能

Load with: base.md + [framework].md
For implementing product analytics with PostHog - event tracking, user identification, feature flags, and project-specific dashboards.

加载方式:base.md + [framework].md
用于基于 PostHog 实现产品分析——包括事件追踪、用户识别、功能标志以及项目专属仪表盘。

Philosophy

核心理念

Measure what matters, not everything.
Analytics should answer specific questions:
  • Are users getting value? (activation, retention)
  • Where do users struggle? (funnels, drop-offs)
  • What features drive engagement? (feature usage)
  • Is the product growing? (acquisition, referrals)
Don't track everything. Track what informs decisions.

衡量重要的指标,而非所有数据。
分析应聚焦于解答特定问题:
  • 用户是否获得价值?(激活、留存)
  • 用户在哪些环节遇到阻碍?(转化漏斗、流失点)
  • 哪些功能能提升用户参与度?(功能使用率)
  • 产品是否在增长?(获客、推荐)
不要追踪所有数据,只追踪能为决策提供依据的内容。

Installation

安装步骤

Next.js (App Router)

Next.js(App Router)

bash
npm install posthog-js
typescript
// lib/posthog.ts
import posthog from 'posthog-js';

export function initPostHog() {
  if (typeof window !== 'undefined' && !posthog.__loaded) {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
      person_profiles: 'identified_only', // Only create profiles for identified users
      capture_pageview: false, // We'll handle this manually for SPA
      capture_pageleave: true,
      loaded: (posthog) => {
        if (process.env.NODE_ENV === 'development') {
          posthog.debug();
        }
      },
    });
  }
  return posthog;
}

export { posthog };
typescript
// app/providers.tsx
'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { initPostHog, posthog } from '@/lib/posthog';

export function PostHogProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    initPostHog();
  }, []);

  // Track pageviews
  useEffect(() => {
    if (pathname) {
      let url = window.origin + pathname;
      if (searchParams.toString()) {
        url += `?${searchParams.toString()}`;
      }
      posthog.capture('$pageview', { $current_url: url });
    }
  }, [pathname, searchParams]);

  return <>{children}</>;
}
typescript
// app/layout.tsx
import { PostHogProvider } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <PostHogProvider>
          {children}
        </PostHogProvider>
      </body>
    </html>
  );
}
bash
npm install posthog-js
typescript
// lib/posthog.ts
import posthog from 'posthog-js';

export function initPostHog() {
  if (typeof window !== 'undefined' && !posthog.__loaded) {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
      person_profiles: 'identified_only', // 仅为已识别用户创建档案
      capture_pageview: false, // 手动处理单页应用的页面浏览追踪
      capture_pageleave: true,
      loaded: (posthog) => {
        if (process.env.NODE_ENV === 'development') {
          posthog.debug();
        }
      },
    });
  }
  return posthog;
}

export { posthog };
typescript
// app/providers.tsx
'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { initPostHog, posthog } from '@/lib/posthog';

export function PostHogProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    initPostHog();
  }, []);

  // 追踪页面浏览
  useEffect(() => {
    if (pathname) {
      let url = window.origin + pathname;
      if (searchParams.toString()) {
        url += `?${searchParams.toString()}`;
      }
      posthog.capture('$pageview', { $current_url: url });
    }
  }, [pathname, searchParams]);

  return <>{children}</>;
}
typescript
// app/layout.tsx
import { PostHogProvider } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <PostHogProvider>
          {children}
        </PostHogProvider>
      </body>
    </html>
  );
}

React (Vite/CRA)

React(Vite/CRA)

typescript
// src/posthog.ts
import posthog from 'posthog-js';

posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
  api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
  person_profiles: 'identified_only',
});

export { posthog };
typescript
// src/main.tsx
import { PostHogProvider } from 'posthog-js/react';
import { posthog } from './posthog';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <PostHogProvider client={posthog}>
    <App />
  </PostHogProvider>
);
typescript
// src/posthog.ts
import posthog from 'posthog-js';

posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
  api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
  person_profiles: 'identified_only',
});

export { posthog };
typescript
// src/main.tsx
import { PostHogProvider } from 'posthog-js/react';
import { posthog } from './posthog';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <PostHogProvider client={posthog}>
    <App />
  </PostHogProvider>
);

Python (FastAPI/Flask)

Python(FastAPI/Flask)

bash
pip install posthog
python
undefined
bash
pip install posthog
python
undefined

analytics/posthog_client.py

analytics/posthog_client.py

import posthog from functools import lru_cache
@lru_cache() def get_posthog(): posthog.project_api_key = os.environ["POSTHOG_API_KEY"] posthog.host = os.environ.get("POSTHOG_HOST", "https://us.i.posthog.com") posthog.debug = os.environ.get("ENV") == "development" return posthog
import posthog from functools import lru_cache import os
@lru_cache() def get_posthog(): posthog.project_api_key = os.environ["POSTHOG_API_KEY"] posthog.host = os.environ.get("POSTHOG_HOST", "https://us.i.posthog.com") posthog.debug = os.environ.get("ENV") == "development" return posthog

Usage

使用示例

def track_event(user_id: str, event: str, properties: dict = None): ph = get_posthog() ph.capture( distinct_id=user_id, event=event, properties=properties or {} )
def identify_user(user_id: str, properties: dict): ph = get_posthog() ph.identify(user_id, properties)
undefined
def track_event(user_id: str, event: str, properties: dict = None): ph = get_posthog() ph.capture( distinct_id=user_id, event=event, properties=properties or {} )
def identify_user(user_id: str, properties: dict): ph = get_posthog() ph.identify(user_id, properties)
undefined

Node.js (Express/Hono)

Node.js(Express/Hono)

bash
npm install posthog-node
typescript
// lib/posthog.ts
import { PostHog } from 'posthog-node';

const posthog = new PostHog(process.env.POSTHOG_API_KEY!, {
  host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
});

// Flush on shutdown
process.on('SIGTERM', () => posthog.shutdown());

export { posthog };

// Usage
export function trackEvent(userId: string, event: string, properties?: Record<string, any>) {
  posthog.capture({
    distinctId: userId,
    event,
    properties,
  });
}

export function identifyUser(userId: string, properties: Record<string, any>) {
  posthog.identify({
    distinctId: userId,
    properties,
  });
}

bash
npm install posthog-node
typescript
// lib/posthog.ts
import { PostHog } from 'posthog-node';

const posthog = new PostHog(process.env.POSTHOG_API_KEY!, {
  host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
});

// 关闭时刷新数据
process.on('SIGTERM', () => posthog.shutdown());

export { posthog };

// 使用示例
export function trackEvent(userId: string, event: string, properties?: Record<string, any>) {
  posthog.capture({
    distinctId: userId,
    event,
    properties,
  });
}

export function identifyUser(userId: string, properties: Record<string, any>) {
  posthog.identify({
    distinctId: userId,
    properties,
  });
}

Environment Variables

环境变量

bash
undefined
bash
undefined

.env.local (Next.js) - SAFE: These are meant to be public

.env.local (Next.js) - 安全:这些变量为公开变量

NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

.env (Backend) - Keep private

.env (后端) - 请保密

POSTHOG_API_KEY=phc_xxxxxxxxxxxxxxxxxxxx POSTHOG_HOST=https://us.i.posthog.com

Add to `credentials.md` patterns:
```python
'POSTHOG_API_KEY': r'phc_[A-Za-z0-9]+',

POSTHOG_API_KEY=phc_xxxxxxxxxxxxxxxxxxxx POSTHOG_HOST=https://us.i.posthog.com

添加到 `credentials.md` 匹配规则:
```python
'POSTHOG_API_KEY': r'phc_[A-Za-z0-9]+',

User Identification

用户识别

When to Identify

识别时机

typescript
// Identify on signup
async function handleSignup(email: string, name: string) {
  const user = await createUser(email, name);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    created_at: user.createdAt,
    plan: 'free',
  });

  posthog.capture('user_signed_up', {
    signup_method: 'email',
  });
}

// Identify on login
async function handleLogin(email: string) {
  const user = await authenticateUser(email);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    plan: user.plan,
    last_login: new Date().toISOString(),
  });

  posthog.capture('user_logged_in');
}

// Reset on logout
function handleLogout() {
  posthog.capture('user_logged_out');
  posthog.reset(); // Clears identity
}
typescript
// 用户注册时识别
async function handleSignup(email: string, name: string) {
  const user = await createUser(email, name);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    created_at: user.createdAt,
    plan: 'free',
  });

  posthog.capture('user_signed_up', {
    signup_method: 'email',
  });
}

// 用户登录时识别
async function handleLogin(email: string) {
  const user = await authenticateUser(email);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    plan: user.plan,
    last_login: new Date().toISOString(),
  });

  posthog.capture('user_logged_in');
}

// 用户登出时重置
function handleLogout() {
  posthog.capture('user_logged_out');
  posthog.reset(); // 清除用户身份
}

User Properties

用户属性

typescript
// Standard properties to track
interface UserProperties {
  // Identity
  email: string;
  name: string;

  // Lifecycle
  created_at: string;
  plan: 'free' | 'pro' | 'enterprise';

  // Engagement
  onboarding_completed: boolean;
  feature_count: number;

  // Business
  company_name?: string;
  company_size?: string;
  industry?: string;
}

// Update properties when they change
posthog.capture('$set', {
  $set: { plan: 'pro' },
});

typescript
// 需追踪的标准属性
interface UserProperties {
  // 身份信息
  email: string;
  name: string;

  // 生命周期
  created_at: string;
  plan: 'free' | 'pro' | 'enterprise';

  // 参与度
  onboarding_completed: boolean;
  feature_count: number;

  // 商业信息
  company_name?: string;
  company_size?: string;
  industry?: string;
}

// 属性变更时更新
posthog.capture('$set', {
  $set: { plan: 'pro' },
});

Event Tracking Patterns

事件追踪模式

Event Naming Convention

事件命名规范

typescript
// Format: [object]_[action]
// Use snake_case, past tense for actions

// ✅ Good event names
'user_signed_up'
'feature_created'
'subscription_upgraded'
'onboarding_completed'
'invite_sent'
'file_uploaded'
'search_performed'
'checkout_started'
'payment_completed'

// ❌ Bad event names
'click'           // Too vague
'ButtonClick'     // Not snake_case
'user signup'     // Spaces
'creatingFeature' // Not past tense
typescript
// 格式:[对象]_[动作]
// 使用蛇形命名法,动作用过去式

// ✅ 规范的事件名称
'user_signed_up'
'feature_created'
'subscription_upgraded'
'onboarding_completed'
'invite_sent'
'file_uploaded'
'search_performed'
'checkout_started'
'payment_completed'

// ❌ 不规范的事件名称
'click'           // 过于模糊
'ButtonClick'     // 未使用蛇形命名法
'user signup'     // 包含空格
'creatingFeature' // 未使用过去式

Core Events by Category

按分类划分的核心事件

typescript
// === AUTHENTICATION ===
posthog.capture('user_signed_up', {
  signup_method: 'google' | 'email' | 'github',
  referral_source: 'organic' | 'paid' | 'referral',
});

posthog.capture('user_logged_in', {
  login_method: 'google' | 'email' | 'magic_link',
});

posthog.capture('user_logged_out');

posthog.capture('password_reset_requested');

// === ONBOARDING ===
posthog.capture('onboarding_started');

posthog.capture('onboarding_step_completed', {
  step_name: 'profile' | 'preferences' | 'first_action',
  step_number: 1,
  total_steps: 3,
});

posthog.capture('onboarding_completed', {
  duration_seconds: 120,
  steps_skipped: 0,
});

posthog.capture('onboarding_skipped', {
  skipped_at_step: 2,
});

// === FEATURE USAGE ===
posthog.capture('feature_used', {
  feature_name: 'export' | 'share' | 'duplicate',
  context: 'dashboard' | 'editor',
});

posthog.capture('[resource]_created', {
  resource_type: 'project' | 'document' | 'team',
  // Resource-specific properties
});

posthog.capture('[resource]_updated', {
  resource_type: 'project',
  fields_changed: ['name', 'description'],
});

posthog.capture('[resource]_deleted', {
  resource_type: 'project',
});

// === BILLING ===
posthog.capture('pricing_page_viewed', {
  current_plan: 'free',
});

posthog.capture('checkout_started', {
  plan: 'pro',
  billing_period: 'monthly' | 'annual',
  price: 29,
});

posthog.capture('subscription_upgraded', {
  from_plan: 'free',
  to_plan: 'pro',
  mrr_change: 29,
});

posthog.capture('subscription_downgraded', {
  from_plan: 'pro',
  to_plan: 'free',
  reason: 'too_expensive' | 'missing_features' | 'not_using',
});

posthog.capture('subscription_cancelled', {
  plan: 'pro',
  reason: 'string',
  feedback: 'string',
});

// === ERRORS ===
posthog.capture('error_occurred', {
  error_type: 'api_error' | 'validation_error' | 'network_error',
  error_message: 'string',
  error_code: 'string',
  page: '/dashboard',
});
typescript
// === 认证 ===
posthog.capture('user_signed_up', {
  signup_method: 'google' | 'email' | 'github',
  referral_source: 'organic' | 'paid' | 'referral',
});

posthog.capture('user_logged_in', {
  login_method: 'google' | 'email' | 'magic_link',
});

posthog.capture('user_logged_out');

posthog.capture('password_reset_requested');

// === 新用户引导 ===
posthog.capture('onboarding_started');

posthog.capture('onboarding_step_completed', {
  step_name: 'profile' | 'preferences' | 'first_action',
  step_number: 1,
  total_steps: 3,
});

posthog.capture('onboarding_completed', {
  duration_seconds: 120,
  steps_skipped: 0,
});

posthog.capture('onboarding_skipped', {
  skipped_at_step: 2,
});

// === 功能使用 ===
posthog.capture('feature_used', {
  feature_name: 'export' | 'share' | 'duplicate',
  context: 'dashboard' | 'editor',
});

posthog.capture('[resource]_created', {
  resource_type: 'project' | 'document' | 'team',
  // 资源专属属性
});

posthog.capture('[resource]_updated', {
  resource_type: 'project',
  fields_changed: ['name', 'description'],
});

posthog.capture('[resource]_deleted', {
  resource_type: 'project',
});

// === 计费 ===
posthog.capture('pricing_page_viewed', {
  current_plan: 'free',
});

posthog.capture('checkout_started', {
  plan: 'pro',
  billing_period: 'monthly' | 'annual',
  price: 29,
});

posthog.capture('subscription_upgraded', {
  from_plan: 'free',
  to_plan: 'pro',
  mrr_change: 29,
});

posthog.capture('subscription_downgraded', {
  from_plan: 'pro',
  to_plan: 'free',
  reason: 'too_expensive' | 'missing_features' | 'not_using',
});

posthog.capture('subscription_cancelled', {
  plan: 'pro',
  reason: 'string',
  feedback: 'string',
});

// === 错误 ===
posthog.capture('error_occurred', {
  error_type: 'api_error' | 'validation_error' | 'network_error',
  error_message: 'string',
  error_code: 'string',
  page: '/dashboard',
});

React Hook for Tracking

React 追踪钩子

typescript
// hooks/useTrack.ts
import { useCallback } from 'react';
import { posthog } from '@/lib/posthog';

export function useTrack() {
  const track = useCallback((event: string, properties?: Record<string, any>) => {
    posthog.capture(event, {
      ...properties,
      timestamp: new Date().toISOString(),
    });
  }, []);

  return { track };
}

// Usage
function CreateProjectButton() {
  const { track } = useTrack();

  const handleCreate = async () => {
    track('project_creation_started');

    try {
      const project = await createProject();
      track('project_created', {
        project_id: project.id,
        template_used: project.template,
      });
    } catch (error) {
      track('project_creation_failed', {
        error_message: error.message,
      });
    }
  };

  return <button onClick={handleCreate}>Create Project</button>;
}

typescript
// hooks/useTrack.ts
import { useCallback } from 'react';
import { posthog } from '@/lib/posthog';

export function useTrack() {
  const track = useCallback((event: string, properties?: Record<string, any>) => {
    posthog.capture(event, {
      ...properties,
      timestamp: new Date().toISOString(),
    });
  }, []);

  return { track };
}

// 使用示例
function CreateProjectButton() {
  const { track } = useTrack();

  const handleCreate = async () => {
    track('project_creation_started');

    try {
      const project = await createProject();
      track('project_created', {
        project_id: project.id,
        template_used: project.template,
      });
    } catch (error) {
      track('project_creation_failed', {
        error_message: error.message,
      });
    }
  };

  return <button onClick={handleCreate}>Create Project</button>;
}

Feature Flags

功能标志

Setup

配置

typescript
// Check feature flag (client-side)
import { useFeatureFlagEnabled } from 'posthog-js/react';

function NewFeature() {
  const showNewUI = useFeatureFlagEnabled('new-dashboard-ui');

  if (showNewUI) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

// With payload
import { useFeatureFlagPayload } from 'posthog-js/react';

function PricingPage() {
  const pricingConfig = useFeatureFlagPayload('pricing-experiment');
  // pricingConfig = { price: 29, showAnnual: true }

  return <Pricing config={pricingConfig} />;
}
typescript
// 检查功能标志(客户端)
import { useFeatureFlagEnabled } from 'posthog-js/react';

function NewFeature() {
  const showNewUI = useFeatureFlagEnabled('new-dashboard-ui');

  if (showNewUI) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

// 带返回值的标志
import { useFeatureFlagPayload } from 'posthog-js/react';

function PricingPage() {
  const pricingConfig = useFeatureFlagPayload('pricing-experiment');
  // pricingConfig = { price: 29, showAnnual: true }

  return <Pricing config={pricingConfig} />;
}

Server-Side (Next.js)

服务端(Next.js)

typescript
// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';
import { cookies } from 'next/headers';

async function getFeatureFlags(userId: string) {
  const posthog = new PostHog(process.env.POSTHOG_API_KEY!);

  const flags = await posthog.getAllFlags(userId);
  await posthog.shutdown();

  return flags;
}

export default async function Dashboard() {
  const cookieStore = cookies();
  const userId = cookieStore.get('user_id')?.value;

  const flags = await getFeatureFlags(userId);

  return (
    <div>
      {flags['new-dashboard'] && <NewFeature />}
    </div>
  );
}
typescript
// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';
import { cookies } from 'next/headers';

async function getFeatureFlags(userId: string) {
  const posthog = new PostHog(process.env.POSTHOG_API_KEY!);

  const flags = await posthog.getAllFlags(userId);
  await posthog.shutdown();

  return flags;
}

export default async function Dashboard() {
  const cookieStore = cookies();
  const userId = cookieStore.get('user_id')?.value;

  const flags = await getFeatureFlags(userId);

  return (
    <div>
      {flags['new-dashboard'] && <NewFeature />}
    </div>
  );
}

A/B Testing

A/B 测试

typescript
// Track experiment exposure
function ExperimentComponent() {
  const variant = useFeatureFlagEnabled('checkout-experiment');

  useEffect(() => {
    posthog.capture('experiment_viewed', {
      experiment: 'checkout-experiment',
      variant: variant ? 'test' : 'control',
    });
  }, [variant]);

  return variant ? <NewCheckout /> : <OldCheckout />;
}

typescript
// 追踪实验曝光
function ExperimentComponent() {
  const variant = useFeatureFlagEnabled('checkout-experiment');

  useEffect(() => {
    posthog.capture('experiment_viewed', {
      experiment: 'checkout-experiment',
      variant: variant ? 'test' : 'control',
    });
  }, [variant]);

  return variant ? <NewCheckout /> : <OldCheckout />;
}

Project-Specific Dashboards

项目专属仪表盘

SaaS Product

SaaS 产品

markdown
undefined
markdown
undefined

Essential SaaS Dashboards

SaaS 核心仪表盘

1. Acquisition Dashboard

1. 获客仪表盘

Questions answered: Where do users come from? What converts?
Insights to create:
  • Signups by source (daily/weekly trend)
  • Signup conversion rate by landing page
  • Time from first visit to signup
  • Signup funnel: Visit → Signup Page → Form Start → Complete
解答问题: 用户从哪里来?哪些渠道转化率高?
需创建的洞察:
  • 按来源划分的注册量(日/周趋势)
  • 按落地页划分的注册转化率
  • 从首次访问到注册的时长
  • 注册漏斗:访问 → 注册页 → 开始填写表单 → 完成注册

2. Activation Dashboard

2. 激活仪表盘

Questions answered: Are new users getting value?
Insights to create:
  • Onboarding completion rate
  • Time to first key action
  • Activation rate (% reaching "aha moment" in first 7 days)
  • Drop-off by onboarding step
  • Feature adoption in first session
解答问题: 新用户是否获得价值?
需创建的洞察:
  • 新用户引导完成率
  • 首次关键操作的时长
  • 激活率(7天内达到"惊喜时刻"的用户占比)
  • 新用户引导各步骤的流失率
  • 首次会话的功能采用情况

3. Engagement Dashboard

3. 参与度仪表盘

Questions answered: How are users using the product?
Insights to create:
  • DAU/WAU/MAU trends
  • Feature usage heatmap
  • Session duration distribution
  • Actions per session
  • Power users vs casual users
解答问题: 用户如何使用产品?
需创建的洞察:
  • DAU/WAU/MAU 趋势
  • 功能使用热力图
  • 会话时长分布
  • 每次会话的操作次数
  • 核心用户 vs 普通用户

4. Retention Dashboard

4. 留存仪表盘

Questions answered: Are users coming back?
Insights to create:
  • Retention cohorts (D1, D7, D30)
  • Churn rate by plan
  • Reactivation rate
  • Last action before churn
  • Features correlated with retention
解答问题: 用户是否会回访?
需创建的洞察:
  • 留存群组(D1、D7、D30)
  • 按套餐划分的流失率
  • 重激活率
  • 流失前的最后操作
  • 与留存相关的功能

5. Revenue Dashboard

5. 营收仪表盘

Questions answered: Is the business growing?
Insights to create:
  • MRR trend
  • Upgrades vs downgrades
  • Trial to paid conversion
  • Revenue by plan
  • LTV by acquisition source
undefined
解答问题: 业务是否在增长?
需创建的洞察:
  • MRR 趋势
  • 升级 vs 降级情况
  • 试用转付费转化率
  • 按套餐划分的营收
  • 按获客来源划分的用户生命周期价值
undefined

E-Commerce

电商产品

markdown
undefined
markdown
undefined

Essential E-Commerce Dashboards

电商核心仪表盘

1. Conversion Funnel

1. 转化漏斗

Insights to create:
  • Full funnel: Browse → PDP → Add to Cart → Checkout → Purchase
  • Cart abandonment rate
  • Checkout drop-off by step
  • Payment failure rate
需创建的洞察:
  • 完整漏斗:浏览 → 商品详情页 → 加入购物车 → 结账 → 购买
  • 购物车弃购率
  • 结账各步骤的流失率
  • 支付失败率

2. Product Performance

2. 商品表现

Insights to create:
  • Product views → purchases (by product)
  • Add to cart rate by category
  • Search → purchase correlation
  • Cross-sell effectiveness
需创建的洞察:
  • 商品浏览 → 购买转化(按商品)
  • 按分类划分的加入购物车率
  • 搜索 → 购买的相关性
  • 交叉销售效果

3. Customer Dashboard

3. 客户仪表盘

Insights to create:
  • Repeat purchase rate
  • Average order value trend
  • Customer lifetime value
  • Purchase frequency distribution
undefined
需创建的洞察:
  • 复购率
  • 平均订单价值趋势
  • 客户生命周期价值
  • 购买频率分布
undefined

Content/Media

内容/媒体产品

markdown
undefined
markdown
undefined

Essential Content Dashboards

内容核心仪表盘

1. Consumption Dashboard

1. 消费仪表盘

Insights to create:
  • Content views by type
  • Read/watch completion rate
  • Time on content
  • Scroll depth distribution
需创建的洞察:
  • 按类型划分的内容浏览量
  • 阅读/观看完成率
  • 内容停留时长
  • 滚动深度分布

2. Engagement Dashboard

2. 参与度仪表盘

Insights to create:
  • Shares by content
  • Comments per article
  • Save/bookmark rate
  • Return visits to same content
需创建的洞察:
  • 按内容划分的分享量
  • 每篇文章的评论数
  • 收藏/书签率
  • 同一内容的回访量

3. Growth Dashboard

3. 增长仪表盘

Insights to create:
  • New vs returning visitors
  • Email signup rate
  • Referral traffic sources
undefined
需创建的洞察:
  • 新访客 vs 回访访客
  • 邮件注册率
  • 推荐流量来源
undefined

AI/LLM Application

AI/LLM 应用

markdown
undefined
markdown
undefined

Essential AI App Dashboards

AI 应用核心仪表盘

1. Usage Dashboard

1. 使用量仪表盘

Insights to create:
  • Queries per user per day
  • Token usage distribution
  • Response time p50/p95
  • Error rate by query type
需创建的洞察:
  • 每位用户每日查询次数
  • Token 使用分布
  • 响应时间 p50/p95
  • 按查询类型划分的错误率

2. Quality Dashboard

2. 质量仪表盘

Insights to create:
  • User feedback (thumbs up/down)
  • Regeneration rate (user asked for new response)
  • Edit rate (user modified AI output)
  • Follow-up query rate
需创建的洞察:
  • 用户反馈(点赞/点踩)
  • 重新生成率(用户要求新回复)
  • 编辑率(用户修改 AI 输出内容)
  • 跟进查询率

3. Cost Dashboard

3. 成本仪表盘

Insights to create:
  • Token cost per user
  • Cost by model
  • Cost by feature
  • Efficiency trends (value/cost)

---
需创建的洞察:
  • 每位用户的 Token 成本
  • 按模型划分的成本
  • 按功能划分的成本
  • 效率趋势(价值/成本)

---

Creating Dashboards

创建仪表盘

Using PostHog MCP

使用 PostHog MCP

markdown
When setting up analytics for a project:

1. First, check existing dashboards:
   - Use `dashboards-get-all` to list current dashboards

2. Create project-appropriate dashboards:
   - Use `dashboard-create` with descriptive name

3. Create insights for each dashboard:
   - Use `query-run` to test queries
   - Use `insight-create-from-query` to save
   - Use `add-insight-to-dashboard` to organize

4. Set up key funnels:
   - Signup funnel
   - Onboarding funnel
   - Purchase/conversion funnel
markdown
为项目设置分析时:

1. 首先,检查现有仪表盘:
   - 使用 `dashboards-get-all` 列出当前所有仪表盘

2. 创建适合项目的仪表盘:
   - 使用 `dashboard-create` 创建带描述性名称的仪表盘

3. 为每个仪表盘创建洞察:
   - 使用 `query-run` 测试查询
   - 使用 `insight-create-from-query` 保存洞察
   - 使用 `add-insight-to-dashboard` 整理洞察

4. 设置核心漏斗:
   - 注册漏斗
   - 新用户引导漏斗
   - 购买/转化漏斗

Dashboard Creation Workflow

仪表盘创建流程

typescript
// Example: Creating SaaS dashboards via MCP

// 1. Create dashboard
const dashboard = await mcp_posthog_dashboard_create({
  name: "Activation Metrics",
  description: "Track new user activation and onboarding",
  tags: ["saas", "activation"],
});

// 2. Create insights
const signupFunnel = await mcp_posthog_query_run({
  query: {
    kind: "InsightVizNode",
    source: {
      kind: "FunnelsQuery",
      series: [
        { kind: "EventsNode", event: "user_signed_up", name: "Signed Up" },
        { kind: "EventsNode", event: "onboarding_started", name: "Started Onboarding" },
        { kind: "EventsNode", event: "onboarding_completed", name: "Completed Onboarding" },
        { kind: "EventsNode", event: "first_value_action", name: "First Value" },
      ],
      dateRange: { date_from: "-30d" },
    },
  },
});

// 3. Save and add to dashboard
const insight = await mcp_posthog_insight_create_from_query({
  name: "Signup to Activation Funnel",
  query: signupFunnel.query,
  favorited: true,
});

await mcp_posthog_add_insight_to_dashboard({
  insightId: insight.id,
  dashboardId: dashboard.id,
});

typescript
// 示例:通过 MCP 创建 SaaS 仪表盘

// 1. 创建仪表盘
const dashboard = await mcp_posthog_dashboard_create({
  name: "激活指标",
  description: "追踪新用户激活与新用户引导情况",
  tags: ["saas", "activation"],
});

// 2. 创建洞察
const signupFunnel = await mcp_posthog_query_run({
  query: {
    kind: "InsightVizNode",
    source: {
      kind: "FunnelsQuery",
      series: [
        { kind: "EventsNode", event: "user_signed_up", name: "已注册" },
        { kind: "EventsNode", event: "onboarding_started", name: "已开始新用户引导" },
        { kind: "EventsNode", event: "onboarding_completed", name: "已完成新用户引导" },
        { kind: "EventsNode", event: "first_value_action", name: "首次关键操作" },
      ],
      dateRange: { date_from: "-30d" },
    },
  },
});

// 3. 保存洞察并添加到仪表盘
const insight = await mcp_posthog_insight_create_from_query({
  name: "注册到激活漏斗",
  query: signupFunnel.query,
  favorited: true,
});

await mcp_posthog_add_insight_to_dashboard({
  insightId: insight.id,
  dashboardId: dashboard.id,
});

Privacy & Compliance

隐私与合规

GDPR Compliance

GDPR 合规

typescript
// Opt-out handling
export function handleCookieConsent(consent: boolean) {
  if (consent) {
    posthog.opt_in_capturing();
  } else {
    posthog.opt_out_capturing();
  }
}

// Check consent status
const hasConsent = posthog.has_opted_in_capturing();

// Initialize with consent check
posthog.init(key, {
  opt_out_capturing_by_default: true, // Require explicit opt-in
  respect_dnt: true, // Respect Do Not Track
});
typescript
// 处理选择退出
export function handleCookieConsent(consent: boolean) {
  if (consent) {
    posthog.opt_in_capturing();
  } else {
    posthog.opt_out_capturing();
  }
}

// 检查同意状态
const hasConsent = posthog.has_opted_in_capturing();

// 初始化时检查同意状态
posthog.init(key, {
  opt_out_capturing_by_default: true, // 需要用户明确选择加入
  respect_dnt: true, // 尊重"不追踪"设置
});

Data to Never Track

绝对不能追踪的数据

typescript
// ❌ NEVER track these
posthog.capture('event', {
  password: '...',           // Credentials
  credit_card: '...',        // Payment info
  ssn: '...',                // Government IDs
  medical_info: '...',       // Health data
  full_address: '...',       // Detailed location
});

// ✅ OK to track
posthog.capture('event', {
  country: 'US',             // General location
  plan: 'pro',               // Product info
  feature_used: 'export',    // Usage
});
typescript
// ❌ 绝对不能追踪以下内容
posthog.capture('event', {
  password: '...',           // 凭证
  credit_card: '...',        // 支付信息
  ssn: '...',                // 政府颁发的身份证件
  medical_info: '...',       // 健康数据
  full_address: '...',       // 详细地址
});

// ✅ 可以追踪的内容
posthog.capture('event', {
  country: 'US',             // 大致位置
  plan: 'pro',               // 产品信息
  feature_used: 'export',    // 使用情况
});

Property Sanitization

属性清理

typescript
// lib/analytics.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'credit', 'ssn'];

function sanitizeProperties(props: Record<string, any>): Record<string, any> {
  return Object.fromEntries(
    Object.entries(props).filter(([key]) =>
      !SENSITIVE_KEYS.some(sensitive => key.toLowerCase().includes(sensitive))
    )
  );
}

export function safeCapture(event: string, properties?: Record<string, any>) {
  posthog.capture(event, sanitizeProperties(properties || {}));
}

typescript
// lib/analytics.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'credit', 'ssn'];

function sanitizeProperties(props: Record<string, any>): Record<string, any> {
  return Object.fromEntries(
    Object.entries(props).filter(([key]) =>
      !SENSITIVE_KEYS.some(sensitive => key.toLowerCase().includes(sensitive))
    )
  );
}

export function safeCapture(event: string, properties?: Record<string, any>) {
  posthog.capture(event, sanitizeProperties(properties || {}));
}

Testing Analytics

测试分析

Development Mode

开发模式

typescript
// Disable in development
if (process.env.NODE_ENV === 'development') {
  posthog.opt_out_capturing();
  // Or use debug mode
  posthog.debug();
}
typescript
// 开发环境中禁用追踪
if (process.env.NODE_ENV === 'development') {
  posthog.opt_out_capturing();
  // 或者使用调试模式
  posthog.debug();
}

E2E Testing

端到端测试

typescript
// playwright/fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  page: async ({ page }, use) => {
    // Mock PostHog to capture events
    await page.addInitScript(() => {
      window.capturedEvents = [];
      window.posthog = {
        capture: (event, props) => {
          window.capturedEvents.push({ event, props });
        },
        identify: () => {},
        reset: () => {},
      };
    });
    await use(page);
  },
});

// In tests
test('tracks signup event', async ({ page }) => {
  await page.goto('/signup');
  await page.fill('[name=email]', 'test@example.com');
  await page.click('button[type=submit]');

  const events = await page.evaluate(() => window.capturedEvents);
  expect(events).toContainEqual({
    event: 'user_signed_up',
    props: expect.objectContaining({ signup_method: 'email' }),
  });
});

typescript
// playwright/fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  page: async ({ page }, use) => {
    // 模拟 PostHog 以捕获事件
    await page.addInitScript(() => {
      window.capturedEvents = [];
      window.posthog = {
        capture: (event, props) => {
          window.capturedEvents.push({ event, props });
        },
        identify: () => {},
        reset: () => {},
      };
    });
    await use(page);
  },
});

// 测试用例
test('追踪注册事件', async ({ page }) => {
  await page.goto('/signup');
  await page.fill('[name=email]', 'test@example.com');
  await page.click('button[type=submit]');

  const events = await page.evaluate(() => window.capturedEvents);
  expect(events).toContainEqual({
    event: 'user_signed_up',
    props: expect.objectContaining({ signup_method: 'email' }),
  });
});

Debugging

调试

PostHog Toolbar

PostHog 工具栏

typescript
// Enable toolbar for debugging
posthog.init(key, {
  // ...
  loaded: (posthog) => {
    if (process.env.NODE_ENV === 'development') {
      posthog.debug();
      // Toolbar available via PostHog dashboard
    }
  },
});
typescript
// 启用工具栏进行调试
posthog.init(key, {
  // ...
  loaded: (posthog) => {
    if (process.env.NODE_ENV === 'development') {
      posthog.debug();
      // 可通过 PostHog 仪表盘访问工具栏
    }
  },
});

Event Debugging

事件调试

typescript
// Log all events in development
posthog.init(key, {
  _onCapture: (eventName, eventData) => {
    if (process.env.NODE_ENV === 'development') {
      console.log('PostHog Event:', eventName, eventData);
    }
  },
});

typescript
// 开发环境中记录所有事件
posthog.init(key, {
  _onCapture: (eventName, eventData) => {
    if (process.env.NODE_ENV === 'development') {
      console.log('PostHog 事件:', eventName, eventData);
    }
  },
});

Quick Reference

快速参考

Event Checklist by User Lifecycle

按用户生命周期划分的事件清单

markdown
undefined
markdown
undefined

Must-Track Events

必须追踪的事件

Acquisition

获客

  • page_viewed
    (automatic with capture_pageview)
  • user_signed_up
  • user_logged_in
  • page_viewed
    (开启 capture_pageview 后自动追踪)
  • user_signed_up
  • user_logged_in

Activation

激活

  • onboarding_started
  • onboarding_step_completed
  • onboarding_completed
  • first_[key_action]
    (your "aha moment")
  • onboarding_started
  • onboarding_step_completed
  • onboarding_completed
  • first_[key_action]
    (你的产品"惊喜时刻")

Engagement

参与度

  • [feature]_used
  • [resource]_created
  • search_performed
  • invite_sent
  • [feature]_used
  • [resource]_created
  • search_performed
  • invite_sent

Revenue

营收

  • pricing_page_viewed
  • checkout_started
  • subscription_upgraded
  • subscription_cancelled
  • pricing_page_viewed
  • checkout_started
  • subscription_upgraded
  • subscription_cancelled

Retention

留存

  • session_started
  • feature_[x]_used
    (power features)
undefined
  • session_started
  • feature_[x]_used
    (核心功能)
undefined

Dashboard Templates

仪表盘模板

Project TypeKey Dashboards
SaaSAcquisition, Activation, Engagement, Retention, Revenue
E-CommerceConversion Funnel, Product Performance, Customer LTV
ContentConsumption, Engagement, Growth
AI/LLMUsage, Quality, Cost
Mobile AppInstalls, Onboarding, DAU/MAU, Crashes
项目类型核心仪表盘
SaaS获客、激活、参与度、留存、营收
电商转化漏斗、商品表现、客户生命周期价值
内容消费、参与度、增长
AI/LLM使用量、质量、成本
移动应用安装量、新用户引导、DAU/MAU、崩溃情况

Properties to Always Include

始终需要包含的属性

typescript
// Auto-enriched by PostHog
$current_url
$browser
$device_type
$os

// Add these yourself
user_plan       // 'free' | 'pro' | 'enterprise'
user_role       // 'admin' | 'member'
company_id      // For B2B
feature_context // Where in the app
typescript
// PostHog 自动补充的属性
$current_url
$browser
$device_type
$os

// 需手动添加的属性
user_plan       // 'free' | 'pro' | 'enterprise'
user_role       // 'admin' | 'member'
company_id      // 适用于 B2B 产品
feature_context // 功能使用场景