How to Add Product Analytics with PostHog 2026
How to Add Product Analytics with PostHog
PostHog is the open-source product analytics platform. One tool replaces five: analytics, session replay, feature flags, A/B testing, and surveys. Self-host it or use their cloud — either way, you own your data.
Most analytics tools require a week of setup before you see useful data. PostHog is designed to be productive from the first day: install the SDK, capture a few key events, and you immediately have funnels, retention curves, and user paths without writing a single SQL query. The feature flag integration is particularly well-designed — it's part of the same SDK, so you can split-test features and measure their impact in a single workflow without stitching together separate tools.
What You'll Build
- Event tracking (pageviews, clicks, custom events)
- Feature flags (gradual rollouts, A/B tests)
- Session replay (watch real user sessions)
- Funnel analysis (signup → activation → conversion)
- User identification and properties
Prerequisites: React/Next.js app, PostHog account (free: 1M events/month, 5K sessions).
1. Setup
Install
npm install posthog-js posthog-node
Initialize (Client-Side)
// lib/posthog.ts
import posthog from 'posthog-js';
export function initPostHog() {
if (typeof window !== 'undefined') {
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, // We'll handle this manually
capture_pageleave: true,
});
}
return posthog;
}
PostHog Provider (Next.js)
// app/providers.tsx
'use client';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
import { useEffect } from 'react';
export function PHProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: '/ingest', // Proxy through your domain (avoids ad blockers)
person_profiles: 'identified_only',
});
}, []);
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}
Add to Layout
// app/layout.tsx
import { PHProvider } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<PHProvider>{children}</PHProvider>
</body>
</html>
);
}
Proxy Configuration (Avoid Ad Blockers)
// next.config.js
module.exports = {
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*',
},
];
},
};
2. Event Tracking
Automatic Pageviews
// components/PostHogPageview.tsx
'use client';
import { usePathname, useSearchParams } from 'next/navigation';
import { usePostHog } from 'posthog-js/react';
import { useEffect } from 'react';
export function PostHogPageview() {
const pathname = usePathname();
const searchParams = useSearchParams();
const posthog = usePostHog();
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname;
if (searchParams.toString()) {
url += `?${searchParams.toString()}`;
}
posthog.capture('$pageview', { $current_url: url });
}
}, [pathname, searchParams, posthog]);
return null;
}
Custom Events
import { usePostHog } from 'posthog-js/react';
function PricingPage() {
const posthog = usePostHog();
const handlePlanSelect = (plan: string) => {
posthog.capture('plan_selected', {
plan_name: plan,
plan_price: getPlanPrice(plan),
source: 'pricing_page',
});
};
const handleCTAClick = () => {
posthog.capture('cta_clicked', {
cta_text: 'Start Free Trial',
page: 'pricing',
});
};
return (
<div>
<button onClick={() => handlePlanSelect('pro')}>Choose Pro</button>
<button onClick={handleCTAClick}>Start Free Trial</button>
</div>
);
}
User Identification
// After login
posthog.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
company: user.company,
});
// After logout
posthog.reset();
Server-Side Tracking
// lib/posthog-server.ts
import { PostHog } from 'posthog-node';
const posthogServer = new PostHog(process.env.POSTHOG_KEY!, {
host: 'https://us.i.posthog.com',
});
// Track server-side events
posthogServer.capture({
distinctId: userId,
event: 'subscription_created',
properties: {
plan: 'pro',
amount: 2900,
currency: 'usd',
billing_period: 'monthly',
},
});
// Flush before process exits
await posthogServer.shutdown();
3. Feature Flags
Create a Flag
In PostHog Dashboard → Feature Flags → New Feature Flag:
- Key:
new-pricing-page - Rollout: 50% of users (or specific conditions)
Check Flag (Client)
import { usePostHog, useFeatureFlagEnabled } from 'posthog-js/react';
function PricingPage() {
const newPricing = useFeatureFlagEnabled('new-pricing-page');
if (newPricing) {
return <NewPricingPage />;
}
return <OldPricingPage />;
}
Check Flag (Server)
// In API route or server component
import { posthogServer } from '@/lib/posthog-server';
const isEnabled = await posthogServer.isFeatureEnabled(
'new-pricing-page',
userId
);
Flag with Payload
// PostHog dashboard: set payload as JSON
const flagPayload = posthog.getFeatureFlagPayload('pricing-experiment');
// Returns: { discount: 20, cta_text: "Save 20% Today" }
4. Session Replay
Enable Recording
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: '/ingest',
session_recording: {
maskAllInputs: true, // Privacy: mask form inputs
maskTextSelector: '.sensitive', // Mask specific elements
},
});
Control Recording
// Start recording manually
posthog.startSessionRecording();
// Stop recording
posthog.stopSessionRecording();
// Only record for specific users
if (user.plan === 'enterprise') {
posthog.startSessionRecording();
}
5. Funnels and Insights
Define Events for Funnel
Track these events to build a conversion funnel:
// Step 1: Visit pricing page
posthog.capture('pricing_page_viewed');
// Step 2: Select a plan
posthog.capture('plan_selected', { plan: 'pro' });
// Step 3: Enter payment info
posthog.capture('payment_info_entered');
// Step 4: Complete purchase
posthog.capture('purchase_completed', { plan: 'pro', amount: 29 });
In PostHog Dashboard → Insights → New Insight → Funnel:
- Step 1:
pricing_page_viewed - Step 2:
plan_selected - Step 3:
payment_info_entered - Step 4:
purchase_completed
Key Insights to Set Up
| Insight Type | Configuration | Business Value |
|---|---|---|
| Funnel | Signup → Activation → Conversion | Conversion optimization |
| Retention | "Login" event, weekly cohorts | Stickiness measurement |
| Trends | Daily active users, feature usage | Growth tracking |
| Paths | User navigation patterns | UX optimization |
| Stickiness | How many days/week users return | Engagement depth |
6. Group Analytics
Track company-level metrics (B2B):
// Associate user with a company
posthog.group('company', 'company_123', {
name: 'Acme Corp',
plan: 'enterprise',
employee_count: 500,
});
// Events are now attributed to both user and company
posthog.capture('feature_used', { feature: 'api_dashboard' });
Self-Hosted vs Cloud
| Factor | Cloud | Self-Hosted |
|---|---|---|
| Setup | Instant | Docker Compose or Kubernetes |
| Maintenance | PostHog handles it | You manage updates |
| Data location | US/EU | Your infrastructure |
| Pricing | Usage-based | Free (infrastructure costs only) |
| Scale | Unlimited | Depends on your infra |
| Best for | Most teams | Strict data residency requirements |
Self-Hosted Quick Start
# Docker Compose (recommended for small teams)
git clone https://github.com/PostHog/posthog.git
cd posthog
docker compose -f docker-compose.hobby.yml up -d
Pricing (Cloud)
| Feature | Free | Paid |
|---|---|---|
| Events | 1M/month | $0.00031/event after 1M |
| Session replay | 5,000/month | $0.005/recording after 5K |
| Feature flags | 1M requests | $0.0001/request after 1M |
| Surveys | 250 responses | $0.002/response after 250 |
Building a Measurement Framework First
Most teams install PostHog, add posthog.capture() calls wherever they remember, then wonder why their analytics data doesn't tell them anything useful. The problem isn't the tool — it's tracking without a plan. Analytics data is only as useful as the questions it answers.
Before adding a single event, write down three to five specific questions you want to answer. "How do users move from free to paid?" is a question. "What percentage of users who activate the API key feature return the following week?" is a question. "User engagement" is not a question — it's a vague aspiration that leads to tracking 50 events that don't connect to any decision.
Define your North Star metric: The North Star metric is the single number that best captures the value your product delivers to users. For a developer tool, it might be "number of successful API calls per user per week." For a SaaS dashboard, it might be "number of reports viewed per active organization per month." Everything you track should either be the North Star metric or something that leads indicators for it (inputs) or outputs from it (business outcomes). Events that don't connect to this hierarchy are noise.
Event taxonomy: Establish naming conventions before writing any code. Use noun_verb format (user_signed_up, plan_selected, api_key_created) rather than mixing conventions. Decide which properties are required on every event versus optional. Document your event schema — even a simple Notion page listing events, their properties, and the questions they answer will save enormous time later when you're trying to debug funnel drops.
Privacy and GDPR Compliance
PostHog is one of the few analytics tools that makes GDPR compliance genuinely feasible, both because it can be self-hosted (keeping data on EU infrastructure) and because it has built-in tools for data subject requests.
Consent management: GDPR requires a lawful basis for tracking EU users. For most product analytics, the appropriate basis is either consent (user explicitly opts in) or legitimate interest (analytics necessary for product improvement, with an opt-out mechanism). For consent-based tracking, initialize PostHog with opt_in_site_apps: false and only call posthog.opt_in_capturing() after the user accepts your cookie banner. For legitimate interest, initialize normally but provide an accessible opt-out — PostHog's posthog.opt_out_capturing() sets a cookie that persists across sessions.
Masking sensitive data: Session replay is powerful but captures everything visible on screen by default. Always set maskAllInputs: true and additionally mask elements containing PII with the data-ph-no-capture attribute or maskTextSelector option. For B2B apps where company data is visible in the UI, mask any elements that show revenue figures, customer names, or account-level data by default. EU-based users of your product may have contractual rights to know their data isn't being captured in session recordings — design your replay configuration accordingly.
Data deletion and portability: When a user requests deletion of their data (a GDPR Article 17 right), PostHog provides an API to delete all events associated with a distinct ID. Test this flow before you need it — the last thing you want is to receive a formal deletion request and discover your deletion workflow is broken. For self-hosted installations, you have full control over data retention; set automatic deletion policies for events older than your retention requirement (typically 25 months for analytics data under GDPR guidance).
PostHog vs Mixpanel vs Amplitude
PostHog is the right choice for most developer-focused teams, but two other strong players deserve consideration depending on your specific needs.
Choose PostHog when: you want one tool that covers analytics, session replay, feature flags, and surveys; you need self-hosting for data residency; you're a small-to-medium team with budget constraints (the free tier is genuinely generous at 1M events/month); or you want to avoid vendor lock-in on your analytics infrastructure. PostHog's open-source nature means you can inspect, fork, and modify the tracking code — a meaningful advantage for security-conscious organizations.
Choose Mixpanel when: you need the most sophisticated funnel and retention analysis tooling; your team includes non-technical stakeholders who need to build their own reports without engineering help; or you're in a high-volume B2C context where Mixpanel's per-user analytics model (rather than PostHog's event-first model) fits your data better. Mixpanel's free tier supports 20M events/month, which beats PostHog at volume but without the session replay and feature flag features.
Choose Amplitude when: you're in a data-mature organization with a dedicated analytics team that needs advanced behavioral cohort analysis, predictive analytics, and deep integration with a data warehouse; you have 50+ active stakeholders using the analytics platform; or you need Amplitude's Experiment product for sophisticated A/B testing. Amplitude is enterprise-oriented and priced accordingly — at scale, it costs significantly more than PostHog or Mixpanel but provides correspondingly deeper features.
Methodology
PostHog's person_profiles: 'identified_only' option (shown in the setup code above) prevents PostHog from creating anonymous profiles for unauthenticated users, which can significantly reduce event volume and cost for apps where anonymous browsing is common. The proxy configuration through Next.js rewrites routes events through your own domain, preventing ad-blocking extensions from blocking PostHog's tracking scripts — this typically recovers 25-40% of events that would otherwise be lost. PostHog Cloud is hosted in the US (us.i.posthog.com) and EU (eu.i.posthog.com); EU Cloud stores data in Frankfurt and is the appropriate choice for apps serving primarily EU users under GDPR. PostHog's session replay free tier (5,000 recordings/month) counts a "session" as a continuous user session; a user who visits multiple pages in one session counts as one recording.
| Not identifying users | Can't connect pre/post-login behavior | Call identify() after auth |
| Client-only tracking | Missed server events | Use posthog-node for server events |
| Not using proxy | Ad blockers block tracking (30-40%) | Proxy through your domain |
| No event naming convention | Messy data, hard to query | Use snake_case consistently |
| Recording everything | Privacy concerns, storage costs | Only record opt-in or specific flows |
Choosing analytics? Compare PostHog vs Mixpanel vs Amplitude on APIScout — open-source vs proprietary, features, and pricing.
Related: Building a SaaS Backend, Build a Real-Time Dashboard with the Mixpanel API, Set Up Segment for Customer Data in 2026