Implement PostHog analytics, feature flags, and session replay for Next.js apps. Use this skill for event tracking, user identification, A/B testing, experiments, and session recording setup. Also handles analytics reporting (funnel analysis, retention, SEO) with Google Search Console integration.
Load these files as needed based on the task:
| File | Load When |
|------|-----------|
| references/nextjs-implementation.md | Setting up PostHog from scratch, detailed code patterns |
| references/event-taxonomy.md | Designing event naming conventions, property patterns |
| references/feature-flags.md | Implementing feature flags, A/B tests, experiments |
# .env.local
NEXT_PUBLIC_POSTHOG_KEY=phc_your_project_key
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
IMPORTANT: Ad blockers block direct PostHog requests. Use a reverse proxy to route through your own domain.
Add to next.config.ts:
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: "/ingest/static/:path*",
destination: "https://us-assets.i.posthog.com/static/:path*",
},
{
source: "/ingest/:path*",
destination: "https://us.i.posthog.com/:path*",
},
{
source: "/ingest/decide",
destination: "https://us.i.posthog.com/decide",
},
];
},
// ... rest of config
};
Also update CSP headers to allow PostHog connections:
"connect-src 'self' ... https://*.posthog.com https://us.i.posthog.com https://us-assets.i.posthog.com",
Create app/providers.tsx:
'use client'
import posthog from 'posthog-js'
import { PostHogProvider as PHProvider } from 'posthog-js/react'
import { useEffect } from 'react'
export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
// Use reverse proxy to bypass ad blockers
api_host: '/ingest',
ui_host: 'https://us.i.posthog.com',
defaults: '2025-05-24',
capture_pageview: false, // We handle manually for accurate funnels
person_profiles: 'identified_only',
})
}, [])
return <PHProvider client={posthog}>{children}</PHProvider>
}
Create lib/posthog-server.ts:
import { PostHog } from 'posthog-node'
let posthogClient: PostHog | null = null
export function getPostHogServer(): PostHog {
if (!posthogClient) {
posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
flushAt: 1,
flushInterval: 0,
})
}
return posthogClient
}
| Pattern | Example | Use Case |
|---------|---------|----------|
| category:object_action | signup:form_submit | User actions |
| feature:action | dashboard:project_create | Feature usage |
| lifecycle:event | user:signup_complete | User journey |
| Pattern | Example | Type |
|---------|---------|------|
| object_adjective | user_id, item_price | Any |
| is_ prefix | is_subscribed, is_first_time | Boolean |
| has_ prefix | has_seen_onboarding | Boolean |
| _count suffix | item_count, generation_count | Number |
| _at suffix | created_at, upgraded_at | Timestamp |
Where to track?
├── User action in browser → Client (posthog-js)
├── API route / webhook → Server (posthog-node)
├── Server Component render → Server (posthog-node)
├── Need 100% accuracy → Server (no ad blockers)
└── Real-time UI feedback → Client (posthog-js)
| Pitfall | Solution |
|---------|----------|
| Ad blockers blocking PostHog | Use reverse proxy (/ingest → PostHog). See setup above |
| Events not appearing | Check ad blockers, verify API key, use reverse proxy |
| Duplicate pageviews | Use capture_pageview: false and handle manually |
| Feature flag flicker | Bootstrap flags via middleware |
| Missing user data | Call identify() BEFORE $pageview for accurate funnels |
| Inconsistent naming | Use category:object_action pattern |
| Failed to fetch errors | Browser extension blocking - use reverse proxy |
| 503 from us-assets.i.posthog.com | Ad blocker injecting fake response - use reverse proxy |
Before implementing PostHog, ask:
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer