TL;DR
PostHog is the open-source product analytics suite — event tracking, session replay, feature flags, A/B testing, surveys, all in one platform, self-hostable. Mixpanel is the event-based analytics platform — powerful funnels, retention analysis, user flows, interactive reports, the pioneer in product analytics. Amplitude is the enterprise analytics platform — behavioral analytics, cohort analysis, data governance, experimentation, used by large product teams. In 2026: PostHog for all-in-one open-source analytics, Mixpanel for focused event analytics with great UX, Amplitude for enterprise behavioral analytics.
Key Takeaways
- PostHog: posthog-js ~500K weekly downloads — open-source, all-in-one, self-hostable
- Mixpanel: mixpanel-browser ~500K weekly downloads — event analytics, funnels, flows
- Amplitude: @amplitude/analytics-browser ~300K weekly downloads — behavioral, cohorts, enterprise
- PostHog bundles analytics + session replay + feature flags + surveys
- Mixpanel has the most intuitive analytics UI
- Amplitude excels at enterprise features and data governance
PostHog
PostHog — open-source product analytics:
Setup
import posthog from "posthog-js"
// Initialize:
posthog.init(process.env.POSTHOG_KEY!, {
api_host: "https://us.i.posthog.com", // Or self-hosted URL
person_profiles: "identified_only",
capture_pageview: true,
capture_pageleave: true,
autocapture: true, // Auto-track clicks, form submissions
})
Event tracking
import posthog from "posthog-js"
// Track event:
posthog.capture("package_compared", {
packages: ["react", "vue"],
comparison_type: "downloads",
})
// Track with groups:
posthog.capture("report_generated", {
report_type: "monthly",
packages_count: 10,
$groups: { organization: "org-123" },
})
// Identify user:
posthog.identify("user-123", {
email: "royce@example.com",
plan: "pro",
signup_date: "2026-01-15",
})
// Set user properties:
posthog.people.set({
last_active: new Date().toISOString(),
packages_tracked: 25,
})
// Page view:
posthog.capture("$pageview", {
$current_url: window.location.href,
})
Feature flags
import posthog from "posthog-js"
// Check feature flag:
if (posthog.isFeatureEnabled("new-comparison-ui")) {
renderNewComparisonUI()
} else {
renderClassicUI()
}
// Get flag payload:
const variant = posthog.getFeatureFlag("pricing-experiment")
// "control" | "variant-a" | "variant-b"
// React hook:
import { useFeatureFlagEnabled } from "posthog-js/react"
function ComparisonPage() {
const showNewUI = useFeatureFlagEnabled("new-comparison-ui")
return showNewUI ? <NewUI /> : <ClassicUI />
}
Session replay and surveys
import posthog from "posthog-js"
// Session replay is automatic (configured in PostHog dashboard)
// Records user sessions for debugging and UX analysis
// Trigger survey programmatically:
posthog.capture("$survey_shown", {
$survey_id: "survey-123",
})
// React:
import { PostHogProvider } from "posthog-js/react"
function App() {
return (
<PostHogProvider client={posthog}>
<Router />
</PostHogProvider>
)
}
Mixpanel
Mixpanel — event-based analytics:
Setup
import mixpanel from "mixpanel-browser"
// Initialize:
mixpanel.init(process.env.MIXPANEL_TOKEN!, {
debug: process.env.NODE_ENV === "development",
track_pageview: "url-with-path",
persistence: "localStorage",
ignore_dnt: false,
})
Event tracking
import mixpanel from "mixpanel-browser"
// Track event:
mixpanel.track("Package Compared", {
packages: ["react", "vue"],
comparison_type: "downloads",
result_count: 2,
})
// Track with timing:
const startTime = Date.now()
await generateReport()
mixpanel.track("Report Generated", {
duration_ms: Date.now() - startTime,
report_type: "monthly",
})
// Identify user:
mixpanel.identify("user-123")
// Set user profile:
mixpanel.people.set({
$email: "royce@example.com",
$name: "Royce",
plan: "pro",
signup_date: "2026-01-15",
packages_tracked: 25,
})
// Increment property:
mixpanel.people.increment("comparisons_made", 1)
// Track revenue:
mixpanel.people.track_charge(29.99, {
plan: "pro",
billing_cycle: "monthly",
})
Funnels and groups
import mixpanel from "mixpanel-browser"
// Track funnel steps:
mixpanel.track("Signup Started")
// ... user fills form ...
mixpanel.track("Signup Email Entered", { method: "email" })
// ... user completes ...
mixpanel.track("Signup Completed", { plan: "free" })
// Group analytics:
mixpanel.set_group("organization", "org-123")
mixpanel.get_group("organization", "org-123").set({
name: "PkgPulse Team",
plan: "enterprise",
member_count: 15,
})
// Super properties (sent with every event):
mixpanel.register({
app_version: "2.1.0",
platform: "web",
theme: "dark",
})
// Time events:
mixpanel.time_event("Search")
// ... user searches ...
mixpanel.track("Search", { query: "react", results: 150 })
// Automatically includes $duration
React integration
import { MixpanelProvider, useMixpanel } from "react-mixpanel-browser"
function App() {
return (
<MixpanelProvider token={process.env.MIXPANEL_TOKEN!}>
<Router />
</MixpanelProvider>
)
}
function ComparisonButton({ packages }: { packages: string[] }) {
const mixpanel = useMixpanel()
return (
<button onClick={() => {
mixpanel.track("Compare Clicked", { packages })
}}>
Compare
</button>
)
}
Amplitude
Amplitude — behavioral analytics:
Setup
import * as amplitude from "@amplitude/analytics-browser"
// Initialize:
amplitude.init(process.env.AMPLITUDE_API_KEY!, {
defaultTracking: {
sessions: true,
pageViews: true,
formInteractions: true,
fileDownloads: true,
},
})
Event tracking
import * as amplitude from "@amplitude/analytics-browser"
// Track event:
amplitude.track("Package Compared", {
packages: ["react", "vue"],
comparison_type: "downloads",
})
// Identify user:
const identifyEvent = new amplitude.Identify()
identifyEvent.set("email", "royce@example.com")
identifyEvent.set("plan", "pro")
identifyEvent.set("signup_date", "2026-01-15")
identifyEvent.setOnce("first_platform", "web")
identifyEvent.add("comparisons_made", 1)
amplitude.identify(identifyEvent)
// Set user ID:
amplitude.setUserId("user-123")
// Revenue tracking:
const revenue = new amplitude.Revenue()
.setProductId("pro-plan")
.setPrice(29.99)
.setQuantity(1)
.setRevenueType("subscription")
amplitude.revenue(revenue)
// Group analytics:
amplitude.setGroup("organization", "org-123")
Advanced tracking
import * as amplitude from "@amplitude/analytics-browser"
// User properties with operations:
const identify = new amplitude.Identify()
identify.set("plan", "pro")
identify.append("used_features", "comparison")
identify.prepend("recent_searches", "react")
identify.add("session_count", 1)
identify.unset("trial_expired")
amplitude.identify(identify)
// Flush events:
amplitude.flush()
// Track with groups:
amplitude.track("Report Downloaded", {
format: "pdf",
pages: 5,
}, {
groups: { organization: "org-123" },
})
// Session replay (Amplitude Session Replay):
amplitude.init(API_KEY, {
defaultTracking: { sessions: true },
plugins: [
amplitude.sessionReplayPlugin({
sampleRate: 0.1, // Record 10% of sessions
}),
],
})
React integration
import { useEffect } from "react"
import * as amplitude from "@amplitude/analytics-browser"
// Track page views:
function usePageTracking() {
useEffect(() => {
amplitude.track("Page Viewed", {
path: window.location.pathname,
referrer: document.referrer,
})
}, [])
}
// Track component interactions:
function PackageCard({ pkg }: { pkg: Package }) {
return (
<div
onClick={() => {
amplitude.track("Package Card Clicked", {
package_name: pkg.name,
position: pkg.rank,
})
}}
>
<h3>{pkg.name}</h3>
<p>{pkg.downloads.toLocaleString()} downloads</p>
</div>
)
}
// Experiment (A/B testing):
import { Experiment } from "@amplitude/experiment-js-client"
const experiment = Experiment.initializeWithAmplitudeAnalytics(
process.env.AMPLITUDE_DEPLOYMENT_KEY!
)
await experiment.fetch()
const variant = experiment.variant("new-pricing-page")
if (variant.value === "treatment") {
renderNewPricing()
}
Feature Comparison
| Feature | PostHog | Mixpanel | Amplitude |
|---|---|---|---|
| Open-source | ✅ | ❌ | ❌ |
| Self-hosted | ✅ | ❌ | ❌ |
| Event tracking | ✅ | ✅ | ✅ |
| Funnels | ✅ | ✅ | ✅ |
| Retention | ✅ | ✅ | ✅ |
| User flows | ✅ | ✅ | ✅ (Journeys) |
| Cohort analysis | ✅ | ✅ | ✅ |
| Session replay | ✅ (built-in) | ❌ | ✅ (add-on) |
| Feature flags | ✅ (built-in) | ❌ | ❌ |
| A/B testing | ✅ (built-in) | ❌ | ✅ (Experiment) |
| Surveys | ✅ (built-in) | ❌ | ❌ |
| Autocapture | ✅ | ✅ (limited) | ✅ |
| Data governance | ❌ | ✅ | ✅ (Taxonomy) |
| Group analytics | ✅ | ✅ | ✅ |
| SQL access | ✅ (HogQL) | ❌ | ❌ |
| Free tier | 1M events/mo | 20M events/mo | 50K MTUs |
| Pricing | Usage-based | Event-based | MTU-based |
When to Use Each
Use PostHog if:
- Want an all-in-one platform (analytics + replay + flags + surveys)
- Prefer open-source and self-hosting option
- Need feature flags and A/B testing alongside analytics
- Want to own your data (GDPR, data sovereignty)
Use Mixpanel if:
- Want the best event analytics UI and query builder
- Need powerful funnel analysis and user flows
- Building growth-focused products with detailed behavioral tracking
- Want the most generous free tier (20M events/month)
Use Amplitude if:
- Building enterprise products with large data volumes
- Need advanced behavioral analytics and cohort analysis
- Want data governance and taxonomy management
- Need experimentation platform (A/B testing)
Data Ownership, Privacy, and Compliance
The most structurally significant difference between these platforms is PostHog's self-hosting option. PostHog can run on your own infrastructure — a single Docker Compose file handles the full stack (ClickHouse for analytics storage, Redis, Celery workers) — meaning event data never leaves your servers. For companies subject to GDPR, HIPAA, or data residency requirements, this eliminates the data processing agreement complexity and cross-border transfer concerns that cloud-only vendors require. PostHog Cloud is also available (hosted in the US or EU), but the option to self-host gives legal teams an exit ramp that Mixpanel and Amplitude can't match.
Mixpanel introduced the EU Data Residency option in 2023, and Amplitude has a similar EU-region offering, but both are cloud-only — your data still lives on their infrastructure, governed by their data processing agreements. For most SaaS companies this is acceptable, but regulated industries (healthcare, fintech, defense) often require more control. Mixpanel's data deletion API and user opt-out mechanisms are mature and well-documented, which helps with GDPR right-to-erasure requests. Amplitude's data governance features (event planning, schema enforcement via the Taxonomy feature) go further: they let you define which properties are PII, set retention policies per property type, and enforce a naming convention before events are ever ingested.
Autocapture behavior also has privacy implications. PostHog's autocapture records user interactions (clicks, inputs, page navigation) automatically, which can inadvertently capture form field values if you're not careful about masking sensitive inputs. All three platforms provide DOM element masking configurations for session replay and autocapture, but PostHog requires more deliberate configuration of its session replay privacy settings since it's capturing more by default.
Pricing Models and Free Tier Comparisons
The three platforms use fundamentally different pricing units, which makes apples-to-apples comparison difficult. PostHog charges per event ingested (first 1M events/month free, then usage-based). Mixpanel charges per Monthly Tracked User (MTU) — an active user who sends at least one event in a month — with 20M events/month on the free tier regardless of user count. Amplitude charges per MTU with a 50K MTU free tier.
In practice, the right choice depends on your product's usage patterns. A B2B SaaS tool with 500 enterprise users who each generate thousands of events will have very low MTU counts (good for Mixpanel and Amplitude pricing) but high event volume (potentially expensive for PostHog). A consumer app with millions of casual users who each fire a handful of events per month will have high MTU counts but lower event volume, favoring PostHog's event-based model. Mixpanel's 20M events/month free tier is the most generous for startups — many early-stage products never exceed it.
PostHog's pricing also bundles feature flags, session replay, A/B tests, and surveys into the same usage-based model: you pay for the events you send regardless of which features consume them. Amplitude and Mixpanel charge separately for experimentation (Amplitude Experiment is a separate product tier) and session replay. This bundling makes PostHog genuinely cheaper for teams that would otherwise pay for multiple point solutions.
SDK Architecture and Integration Patterns
All three SDKs follow a singleton initialization pattern (posthog.init(), mixpanel.init(), amplitude.init()), but their React integration approaches differ in ways that matter for Next.js and React Server Components. PostHog provides posthog-js/react with a PostHogProvider context and hooks like useFeatureFlagEnabled and usePostHog, which work correctly in the App Router when wrapped in a client component. Because feature flags require network calls, PostHog supports server-side evaluation via the posthog-node package for RSC environments.
Mixpanel's React integration is community-maintained (react-mixpanel-browser) rather than officially supported, which means updates lag behind the core SDK. For Next.js App Router projects, the recommended pattern is to initialize Mixpanel in a client-only component and use the mixpanel singleton directly in event handlers. Amplitude's browser SDK v2 introduced a plugin architecture that replaces the older monolithic bundle: you load only the features you need (session replay, experiment, autocapture) as separate plugins, which reduces initial bundle size significantly compared to loading everything up front.
The Node.js server-side SDKs (posthog-node, mixpanel, @amplitude/analytics-node) are primarily used for backend event tracking and — in PostHog's case — feature flag evaluation in SSR contexts. If you're building a Next.js app and want to gate routes server-side based on feature flags, posthog-node is the only one of the three that supports this pattern natively, since it includes the full feature flag evaluation logic with local caching via distinctId.
Event Naming Conventions and Schema Governance
One operational challenge that emerges as teams scale beyond a few dozen events is maintaining a consistent event taxonomy. Without governance, a codebase ends up with both ButtonClicked and button_clicked, PackageViewed and view_package — variants that make analysis across features impossible. Amplitude's Taxonomy feature (available on paid plans) enforces naming conventions and property schemas before events are ingested, blocking malformed events at the SDK level. This is the strongest governance model of the three. Mixpanel's Lexicon provides a post-hoc schema registry where you can document what each event means, set property types, and mark events as deprecated — but it doesn't block non-conforming events from being ingested. PostHog takes a more developer-centric approach: schema validation is implemented by the team via property validation in the SDK initialization or through the PostHog API's ingestion filtering capabilities.
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on posthog-js v1.x, mixpanel-browser v2.x, and @amplitude/analytics-browser v2.x.
Compare analytics and developer tooling on PkgPulse →
See also: Lit vs Svelte and AVA vs Jest, Cytoscape.js vs vis-network vs Sigma.js.