PostHog Implementation (Next.js 2025)
What This Skill Covers
-
Analytics - Event tracking, user identification, group analytics
-
Feature Flags - Boolean flags, multivariate, A/B testing
-
Session Replay - Recording setup, privacy controls
-
Analytics Queries - HogQL, Query API, extracting insights
-
Reporting - Funnel analysis, retention, error reports, SEO
Reference Files
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
Quick Setup
Environment Variables
.env.local
NEXT_PUBLIC_POSTHOG_KEY=phc_your_project_key NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
Reverse Proxy Setup (RECOMMENDED)
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",
PostHog Provider (Client-Side)
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> }
PostHog Server Client
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 }
Event Naming Convention
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
Property Naming
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
Server vs Client Decision Tree
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)
Common Pitfalls
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
Clarifying Questions
Before implementing PostHog, ask:
-
What events are most important to track? (signups, conversions, feature usage)
-
Do you need server-side tracking? (for accuracy, API routes)
-
Are you running A/B tests? (need experiment setup)
-
What's your auth provider? (for user identification pattern)
-
Do you need session replay? (privacy considerations)