posthog-analytics
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePostHog 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-jstypescript
// 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-jstypescript
// 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 posthogpython
undefinedbash
pip install posthogpython
undefinedanalytics/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)
undefineddef 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)
undefinedNode.js (Express/Hono)
Node.js(Express/Hono)
bash
npm install posthog-nodetypescript
// 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-nodetypescript
// 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
undefinedbash
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 tensetypescript
// 格式:[对象]_[动作]
// 使用蛇形命名法,动作用过去式
// ✅ 规范的事件名称
'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
undefinedmarkdown
undefinedEssential 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 降级情况
- 试用转付费转化率
- 按套餐划分的营收
- 按获客来源划分的用户生命周期价值
undefinedE-Commerce
电商产品
markdown
undefinedmarkdown
undefinedEssential 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需创建的洞察:
- 复购率
- 平均订单价值趋势
- 客户生命周期价值
- 购买频率分布
undefinedContent/Media
内容/媒体产品
markdown
undefinedmarkdown
undefinedEssential 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 回访访客
- 邮件注册率
- 推荐流量来源
undefinedAI/LLM Application
AI/LLM 应用
markdown
undefinedmarkdown
undefinedEssential 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 funnelmarkdown
为项目设置分析时:
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
undefinedmarkdown
undefinedMust-Track Events
必须追踪的事件
Acquisition
获客
- (automatic with capture_pageview)
page_viewed -
user_signed_up -
user_logged_in
- (开启 capture_pageview 后自动追踪)
page_viewed -
user_signed_up -
user_logged_in
Activation
激活
-
onboarding_started -
onboarding_step_completed -
onboarding_completed - (your "aha moment")
first_[key_action]
-
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 - (power features)
feature_[x]_used
undefined-
session_started - (核心功能)
feature_[x]_used
undefinedDashboard Templates
仪表盘模板
| Project Type | Key Dashboards |
|---|---|
| SaaS | Acquisition, Activation, Engagement, Retention, Revenue |
| E-Commerce | Conversion Funnel, Product Performance, Customer LTV |
| Content | Consumption, Engagement, Growth |
| AI/LLM | Usage, Quality, Cost |
| Mobile App | Installs, 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 apptypescript
// PostHog 自动补充的属性
$current_url
$browser
$device_type
$os
// 需手动添加的属性
user_plan // 'free' | 'pro' | 'enterprise'
user_role // 'admin' | 'member'
company_id // 适用于 B2B 产品
feature_context // 功能使用场景