PostHog Analytics
"PostHog: product analytics, event tracking, feature flags, session recordings, A/B testing, self-hosted/cloud, Next.js SDK"
PostHog is an open-source product analytics platform that combines event tracking, feature flags, session recordings, A/B testing, and more into a single platform. It can be self-hosted or used as a cloud service, giving teams full control over their data. The key principle is to instrument events that represent meaningful user actions, use feature flags to gate rollouts safely, and ## Key Points - **Disable automatic pageview capture** in Next.js and track manually via the router to avoid - **Use `posthog.group()`** for B2B products to associate users with companies and analyze - **Mask sensitive elements** using `data-ph-mask` attributes to prevent PII from appearing in - **Evaluate feature flags server-side** for critical paths like access control to avoid flash - **Shut down the server client** after capturing events in serverless functions to ensure the - **Use flag payloads** to deliver configuration alongside feature flags, reducing the need for - **Set `persistence: "localStorage+cookie"`** to maintain user identity across subdomains while - **Tracking everything without a plan.** Autocapture is useful for discovery, but relying on it - **Calling `posthog.init()` multiple times.** Guard initialization with a loaded check to prevent - **Evaluating feature flags on every render.** Use the React hooks (`useFeatureFlagEnabled`) which - **Ignoring server-side shutdown.** In serverless environments, failing to call `client.shutdown()` - **Storing PII in event properties.** Avoid sending emails, names, or other personal data as event
skilldb get analytics-services-skills/PostHog AnalyticsFull skill: 249 linesPostHog Analytics
Core Philosophy
PostHog is an open-source product analytics platform that combines event tracking, feature flags, session recordings, A/B testing, and more into a single platform. It can be self-hosted or used as a cloud service, giving teams full control over their data. The key principle is to instrument events that represent meaningful user actions, use feature flags to gate rollouts safely, and leverage session recordings plus analytics together to understand both the "what" and the "why" behind user behavior.
Setup
Installation
// Install the PostHog JavaScript SDK and Next.js integration
// npm install posthog-js posthog-node
// lib/posthog.ts — Client-side initialization
import posthog from "posthog-js";
export function initPostHog(): void {
if (typeof window !== "undefined" && !posthog.__loaded) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "https://app.posthog.com",
capture_pageview: false, // We handle this manually in Next.js
capture_pageleave: true,
persistence: "localStorage+cookie",
autocapture: true,
session_recording: {
maskTextSelector: "[data-ph-mask]",
},
});
}
}
export { posthog };
Next.js Provider Setup
// app/providers.tsx
"use client";
import { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import { initPostHog } from "@/lib/posthog";
function PostHogPageView(): null {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (pathname) {
let url = window.origin + pathname;
if (searchParams?.toString()) {
url += `?${searchParams.toString()}`;
}
posthog.capture("$pageview", { $current_url: url });
}
}, [pathname, searchParams]);
return null;
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
initPostHog();
}, []);
return (
<PHProvider client={posthog}>
<PostHogPageView />
{children}
</PHProvider>
);
}
Server-Side Client
// lib/posthog-server.ts
import { PostHog } from "posthog-node";
let posthogServerClient: PostHog | null = null;
export function getPostHogServer(): PostHog {
if (!posthogServerClient) {
posthogServerClient = new PostHog(process.env.POSTHOG_API_KEY!, {
host: process.env.POSTHOG_HOST ?? "https://app.posthog.com",
flushAt: 1,
flushInterval: 0,
});
}
return posthogServerClient;
}
Key Techniques
Event Tracking
// Track custom events with properties
import posthog from "posthog-js";
function trackPurchase(item: { id: string; name: string; price: number }): void {
posthog.capture("purchase_completed", {
item_id: item.id,
item_name: item.name,
price: item.price,
currency: "USD",
});
}
// Identify users after login
function identifyUser(user: { id: string; email: string; plan: string }): void {
posthog.identify(user.id, {
email: user.email,
plan: user.plan,
signed_up_at: new Date().toISOString(),
});
}
// Set group-level properties (for B2B analytics)
function setOrganization(orgId: string, orgName: string): void {
posthog.group("company", orgId, {
name: orgName,
industry: "technology",
});
}
Feature Flags
// Client-side feature flag checks
import { useFeatureFlagEnabled, useFeatureFlagPayload } from "posthog-js/react";
function PricingPage(): JSX.Element {
const showNewPricing = useFeatureFlagEnabled("new-pricing-page");
const pricingConfig = useFeatureFlagPayload("new-pricing-page") as {
tiers: Array<{ name: string; price: number }>;
} | undefined;
if (showNewPricing && pricingConfig) {
return <NewPricingLayout tiers={pricingConfig.tiers} />;
}
return <LegacyPricingLayout />;
}
// Server-side feature flag evaluation
import { getPostHogServer } from "@/lib/posthog-server";
async function getFeatureFlag(userId: string, flagKey: string): Promise<boolean> {
const client = getPostHogServer();
const isEnabled = await client.isFeatureEnabled(flagKey, userId);
return isEnabled ?? false;
}
A/B Testing and Experiments
// Using experiments with automatic exposure tracking
import { useFeatureFlagVariantKey } from "posthog-js/react";
function SignupButton(): JSX.Element {
const variant = useFeatureFlagVariantKey("signup-cta-experiment");
const buttonText: Record<string, string> = {
control: "Sign Up",
"variant-a": "Get Started Free",
"variant-b": "Start Your Trial",
};
return (
<button className="btn-primary">
{buttonText[variant as string] ?? "Sign Up"}
</button>
);
}
Server-Side Event Capture
// Capture events from API routes or server actions
import { getPostHogServer } from "@/lib/posthog-server";
async function handleWebhook(event: WebhookEvent): Promise<void> {
const client = getPostHogServer();
client.capture({
distinctId: event.userId,
event: "subscription_renewed",
properties: {
plan: event.plan,
amount: event.amount,
renewal_date: event.date,
},
});
await client.shutdown();
}
Best Practices
- Disable automatic pageview capture in Next.js and track manually via the router to avoid duplicate events on client-side navigation.
- Use
posthog.group()for B2B products to associate users with companies and analyze behavior at the organization level. - Mask sensitive elements using
data-ph-maskattributes to prevent PII from appearing in session recordings. - Evaluate feature flags server-side for critical paths like access control to avoid flash of incorrect content.
- Shut down the server client after capturing events in serverless functions to ensure the event queue flushes before the function terminates.
- Use flag payloads to deliver configuration alongside feature flags, reducing the need for additional API calls or config files.
- Set
persistence: "localStorage+cookie"to maintain user identity across subdomains while still supporting cross-domain tracking via cookies.
Anti-Patterns
- Tracking everything without a plan. Autocapture is useful for discovery, but relying on it exclusively leads to noisy data. Define a tracking plan with named events for key actions.
- Calling
posthog.init()multiple times. Guard initialization with a loaded check to prevent duplicate instances and double-counted events. - Evaluating feature flags on every render. Use the React hooks (
useFeatureFlagEnabled) which cache results, rather than callingposthog.isFeatureEnabled()directly in render logic. - Ignoring server-side shutdown. In serverless environments, failing to call
client.shutdown()means events may be lost because the function terminates before the queue flushes. - Storing PII in event properties. Avoid sending emails, names, or other personal data as event
properties. Use
posthog.identify()to link user identity separately from event data. - Using feature flags without fallback values. Always provide a default behavior for when the flag evaluation fails or returns undefined, ensuring the application degrades gracefully.
Install this skill directly: skilldb add analytics-services-skills
Related Skills
Amplitude Analytics
"Amplitude: product analytics, behavioral cohorts, funnels, retention, experiment platform, user journeys, JavaScript SDK"
Heap
Heap: autocapture product analytics, session replay, retroactive event definition, funnel and retention analysis, JavaScript SDK
June.so
June.so: B2B product analytics, company-level insights, feature adoption reports, activation tracking, Node and JavaScript SDK
Mixpanel Analytics
"Mixpanel: event-based analytics, funnels, retention, user profiles, cohorts, group analytics, JavaScript/Node SDK"
Plausible Analytics
"Plausible: privacy-friendly analytics, no cookies, lightweight script, custom events, goals, API, self-hosted option"
Segment
Segment: customer data platform, event routing, identity resolution, analytics.js, server-side sources, warehouse destinations