
Posthog Analytics
- 4 installs
- 706 repo stars
- Updated July 14, 2026
- alinaqi/maggy
posthog-analytics is a Claude Code skill for implementing PostHog product analytics, event tracking, user identification, feature flags, and dashboards.
About
posthog-analytics is a reference skill for implementing product analytics with PostHog. It covers installing the SDK across Next.js, React, Python, and Node.js, capturing events, identifying users, feature flags, and dashboards. A developer uses it when adding analytics or feature flags to measure activation, retention, and funnels. It stresses tracking only what informs decisions rather than everything.
- Instrument PostHog product analytics, event tracking, feature flags, and dashboards
- Install snippets for Next.js, React, Python, and Node.js with identified-only profiles
- Philosophy: measure what matters (activation, retention, funnels), not everything
Posthog Analytics by the numbers
- 4 all-time installs (skills.sh)
- Ranked #1,625 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
posthog-analytics capabilities & compatibility
Requires a PostHog project API key; PostHog has a free tier and paid plans.
- Capabilities
- data analysis · event tracking · feature flags · funnels
- Works with
- postgres
- Use cases
- data analysis
- Pricing
- Bring your own API key
What posthog-analytics says it does
PostHog analytics, event tracking, feature flags, dashboards
For implementing product analytics with PostHog - event tracking, user identification, feature flags, and project-specific dashboards.
**Measure what matters, not everything.**
npx skills add https://github.com/alinaqi/maggy --skill posthog-analyticsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 706 |
| Last updated | July 14, 2026 |
| Repository | alinaqi/maggy ↗ |
What it does
Instrument a product with PostHog to capture events, identify users, run feature flags, and build funnels and dashboards for activation and retention.
Who is it for?
Adding product analytics or feature flags to a SaaS or web/mobile app to measure activation and retention.
Skip if: Marketing/SEO web analytics or teams not using PostHog.
When should I use this skill?
When adding analytics, feature flags, or event tracking with PostHog.
What you get
PostHog is installed across the stack, meaningful events and identities are captured, and feature flags and funnels inform decisions.
- PostHog client init for the chosen stack
- Event-tracking and user-identify helpers
By the numbers
- Answers 4 question types: activation/retention, funnels/drop-offs, feature usage, acquisition/referrals
- Uses person_profiles: 'identified_only'
Files
PostHog Analytics Skill
For implementing product analytics with PostHog - event tracking, user identification, feature flags, and project-specific dashboards.
Sources: PostHog Docs | Product Analytics | Feature Flags
---
Philosophy
Measure what matters, not everything.
Analytics should answer specific questions:
- Are users getting value? (activation, retention)
- Where do users struggle? (funnels, drop-offs)
- What features drive engagement? (feature usage)
- Is the product growing? (acquisition, referrals)
Don't track everything. Track what informs decisions.
---
Installation
Next.js (App Router)
npm install posthog-js// lib/posthog.ts
import posthog from 'posthog-js';
export function initPostHog() {
if (typeof window !== 'undefined' && !posthog.__loaded) {
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', // Only create profiles for identified users
capture_pageview: false, // We'll handle this manually for SPA
capture_pageleave: true,
loaded: (posthog) => {
if (process.env.NODE_ENV === 'development') {
posthog.debug();
}
},
});
}
return posthog;
}
export { posthog };// app/providers.tsx
'use client';
import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { initPostHog, posthog } from '@/lib/posthog';
export function PostHogProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
initPostHog();
}, []);
// Track pageviews
useEffect(() => {
if (pathname) {
let url = window.origin + pathname;
if (searchParams.toString()) {
url += `?${searchParams.toString()}`;
}
posthog.capture('$pageview', { $current_url: url });
}
}, [pathname, searchParams]);
return <>{children}</>;
}// app/layout.tsx
import { PostHogProvider } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<PostHogProvider>
{children}
</PostHogProvider>
</body>
</html>
);
}React (Vite/CRA)
// src/posthog.ts
import posthog from 'posthog-js';
posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
person_profiles: 'identified_only',
});
export { posthog };// src/main.tsx
import { PostHogProvider } from 'posthog-js/react';
import { posthog } from './posthog';
ReactDOM.createRoot(document.getElementById('root')!).render(
<PostHogProvider client={posthog}>
<App />
</PostHogProvider>
);Python (FastAPI/Flask)
pip install posthog# analytics/posthog_client.py
import posthog
from functools import lru_cache
@lru_cache()
def get_posthog():
posthog.project_api_key = os.environ["POSTHOG_API_KEY"]
posthog.host = os.environ.get("POSTHOG_HOST", "https://us.i.posthog.com")
posthog.debug = os.environ.get("ENV") == "development"
return posthog
# Usage
def track_event(user_id: str, event: str, properties: dict = None):
ph = get_posthog()
ph.capture(
distinct_id=user_id,
event=event,
properties=properties or {}
)
def identify_user(user_id: str, properties: dict):
ph = get_posthog()
ph.identify(user_id, properties)Node.js (Express/Hono)
npm install posthog-node// lib/posthog.ts
import { PostHog } from 'posthog-node';
const posthog = new PostHog(process.env.POSTHOG_API_KEY!, {
host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
});
// Flush on shutdown
process.on('SIGTERM', () => posthog.shutdown());
export { posthog };
// Usage
export function trackEvent(userId: string, event: string, properties?: Record<string, any>) {
posthog.capture({
distinctId: userId,
event,
properties,
});
}
export function identifyUser(userId: string, properties: Record<string, any>) {
posthog.identify({
distinctId: userId,
properties,
});
}---
Environment Variables
# .env.local (Next.js) - SAFE: These are meant to be public
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# .env (Backend) - Keep private
POSTHOG_API_KEY=phc_xxxxxxxxxxxxxxxxxxxx
POSTHOG_HOST=https://us.i.posthog.comAdd to credentials.md patterns:
'POSTHOG_API_KEY': r'phc_[A-Za-z0-9]+',---
User Identification
When to Identify
// Identify on signup
async function handleSignup(email: string, name: string) {
const user = await createUser(email, name);
posthog.identify(user.id, {
email: user.email,
name: user.name,
created_at: user.createdAt,
plan: 'free',
});
posthog.capture('user_signed_up', {
signup_method: 'email',
});
}
// Identify on login
async function handleLogin(email: string) {
const user = await authenticateUser(email);
posthog.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
last_login: new Date().toISOString(),
});
posthog.capture('user_logged_in');
}
// Reset on logout
function handleLogout() {
posthog.capture('user_logged_out');
posthog.reset(); // Clears identity
}User Properties
// Standard properties to track
interface UserProperties {
// Identity
email: string;
name: string;
// Lifecycle
created_at: string;
plan: 'free' | 'pro' | 'enterprise';
// Engagement
onboarding_completed: boolean;
feature_count: number;
// Business
company_name?: string;
company_size?: string;
industry?: string;
}
// Update properties when they change
posthog.capture('$set', {
$set: { plan: 'pro' },
});---
Event Tracking Patterns
Event Naming Convention
// Format: [object]_[action]
// Use snake_case, past tense for actions
// ✅ Good event names
'user_signed_up'
'feature_created'
'subscription_upgraded'
'onboarding_completed'
'invite_sent'
'file_uploaded'
'search_performed'
'checkout_started'
'payment_completed'
// ❌ Bad event names
'click' // Too vague
'ButtonClick' // Not snake_case
'user signup' // Spaces
'creatingFeature' // Not past tenseCore Events by Category
// === AUTHENTICATION ===
posthog.capture('user_signed_up', {
signup_method: 'google' | 'email' | 'github',
referral_source: 'organic' | 'paid' | 'referral',
});
posthog.capture('user_logged_in', {
login_method: 'google' | 'email' | 'magic_link',
});
posthog.capture('user_logged_out');
posthog.capture('password_reset_requested');
// === ONBOARDING ===
posthog.capture('onboarding_started');
posthog.capture('onboarding_step_completed', {
step_name: 'profile' | 'preferences' | 'first_action',
step_number: 1,
total_steps: 3,
});
posthog.capture('onboarding_completed', {
duration_seconds: 120,
steps_skipped: 0,
});
posthog.capture('onboarding_skipped', {
skipped_at_step: 2,
});
// === FEATURE USAGE ===
posthog.capture('feature_used', {
feature_name: 'export' | 'share' | 'duplicate',
context: 'dashboard' | 'editor',
});
posthog.capture('[resource]_created', {
resource_type: 'project' | 'document' | 'team',
// Resource-specific properties
});
posthog.capture('[resource]_updated', {
resource_type: 'project',
fields_changed: ['name', 'description'],
});
posthog.capture('[resource]_deleted', {
resource_type: 'project',
});
// === BILLING ===
posthog.capture('pricing_page_viewed', {
current_plan: 'free',
});
posthog.capture('checkout_started', {
plan: 'pro',
billing_period: 'monthly' | 'annual',
price: 29,
});
posthog.capture('subscription_upgraded', {
from_plan: 'free',
to_plan: 'pro',
mrr_change: 29,
});
posthog.capture('subscription_downgraded', {
from_plan: 'pro',
to_plan: 'free',
reason: 'too_expensive' | 'missing_features' | 'not_using',
});
posthog.capture('subscription_cancelled', {
plan: 'pro',
reason: 'string',
feedback: 'string',
});
// === ERRORS ===
posthog.capture('error_occurred', {
error_type: 'api_error' | 'validation_error' | 'network_error',
error_message: 'string',
error_code: 'string',
page: '/dashboard',
});React Hook for Tracking
// hooks/useTrack.ts
import { useCallback } from 'react';
import { posthog } from '@/lib/posthog';
export function useTrack() {
const track = useCallback((event: string, properties?: Record<string, any>) => {
posthog.capture(event, {
...properties,
timestamp: new Date().toISOString(),
});
}, []);
return { track };
}
// Usage
function CreateProjectButton() {
const { track } = useTrack();
const handleCreate = async () => {
track('project_creation_started');
try {
const project = await createProject();
track('project_created', {
project_id: project.id,
template_used: project.template,
});
} catch (error) {
track('project_creation_failed', {
error_message: error.message,
});
}
};
return <button onClick={handleCreate}>Create Project</button>;
}---
Feature Flags
Setup
// Check feature flag (client-side)
import { useFeatureFlagEnabled } from 'posthog-js/react';
function NewFeature() {
const showNewUI = useFeatureFlagEnabled('new-dashboard-ui');
if (showNewUI) {
return <NewDashboard />;
}
return <OldDashboard />;
}
// With payload
import { useFeatureFlagPayload } from 'posthog-js/react';
function PricingPage() {
const pricingConfig = useFeatureFlagPayload('pricing-experiment');
// pricingConfig = { price: 29, showAnnual: true }
return <Pricing config={pricingConfig} />;
}Server-Side (Next.js)
// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';
import { cookies } from 'next/headers';
async function getFeatureFlags(userId: string) {
const posthog = new PostHog(process.env.POSTHOG_API_KEY!);
const flags = await posthog.getAllFlags(userId);
await posthog.shutdown();
return flags;
}
export default async function Dashboard() {
const cookieStore = cookies();
const userId = cookieStore.get('user_id')?.value;
const flags = await getFeatureFlags(userId);
return (
<div>
{flags['new-dashboard'] && <NewFeature />}
</div>
);
}A/B Testing
// Track experiment exposure
function ExperimentComponent() {
const variant = useFeatureFlagEnabled('checkout-experiment');
useEffect(() => {
posthog.capture('experiment_viewed', {
experiment: 'checkout-experiment',
variant: variant ? 'test' : 'control',
});
}, [variant]);
return variant ? <NewCheckout /> : <OldCheckout />;
}---
Project-Specific Dashboards
SaaS Product
## Essential SaaS Dashboards
### 1. Acquisition Dashboard
**Questions answered:** Where do users come from? What converts?
Insights to create:
- [ ] Signups by source (daily/weekly trend)
- [ ] Signup conversion rate by landing page
- [ ] Time from first visit to signup
- [ ] Signup funnel: Visit → Signup Page → Form Start → Complete
### 2. Activation Dashboard
**Questions answered:** Are new users getting value?
Insights to create:
- [ ] Onboarding completion rate
- [ ] Time to first key action
- [ ] Activation rate (% reaching "aha moment" in first 7 days)
- [ ] Drop-off by onboarding step
- [ ] Feature adoption in first session
### 3. Engagement Dashboard
**Questions answered:** How are users using the product?
Insights to create:
- [ ] DAU/WAU/MAU trends
- [ ] Feature usage heatmap
- [ ] Session duration distribution
- [ ] Actions per session
- [ ] Power users vs casual users
### 4. Retention Dashboard
**Questions answered:** Are users coming back?
Insights to create:
- [ ] Retention cohorts (D1, D7, D30)
- [ ] Churn rate by plan
- [ ] Reactivation rate
- [ ] Last action before churn
- [ ] Features correlated with retention
### 5. Revenue Dashboard
**Questions answered:** Is the business growing?
Insights to create:
- [ ] MRR trend
- [ ] Upgrades vs downgrades
- [ ] Trial to paid conversion
- [ ] Revenue by plan
- [ ] LTV by acquisition sourceE-Commerce
## Essential E-Commerce Dashboards
### 1. Conversion Funnel
Insights to create:
- [ ] Full funnel: Browse → PDP → Add to Cart → Checkout → Purchase
- [ ] Cart abandonment rate
- [ ] Checkout drop-off by step
- [ ] Payment failure rate
### 2. Product Performance
Insights to create:
- [ ] Product views → purchases (by product)
- [ ] Add to cart rate by category
- [ ] Search → purchase correlation
- [ ] Cross-sell effectiveness
### 3. Customer Dashboard
Insights to create:
- [ ] Repeat purchase rate
- [ ] Average order value trend
- [ ] Customer lifetime value
- [ ] Purchase frequency distributionContent/Media
## Essential Content Dashboards
### 1. Consumption Dashboard
Insights to create:
- [ ] Content views by type
- [ ] Read/watch completion rate
- [ ] Time on content
- [ ] Scroll depth distribution
### 2. Engagement Dashboard
Insights to create:
- [ ] Shares by content
- [ ] Comments per article
- [ ] Save/bookmark rate
- [ ] Return visits to same content
### 3. Growth Dashboard
Insights to create:
- [ ] New vs returning visitors
- [ ] Email signup rate
- [ ] Referral traffic sourcesAI/LLM Application
## Essential AI App Dashboards
### 1. Usage Dashboard
Insights to create:
- [ ] Queries per user per day
- [ ] Token usage distribution
- [ ] Response time p50/p95
- [ ] Error rate by query type
### 2. Quality Dashboard
Insights to create:
- [ ] User feedback (thumbs up/down)
- [ ] Regeneration rate (user asked for new response)
- [ ] Edit rate (user modified AI output)
- [ ] Follow-up query rate
### 3. Cost Dashboard
Insights to create:
- [ ] Token cost per user
- [ ] Cost by model
- [ ] Cost by feature
- [ ] Efficiency trends (value/cost)---
Creating Dashboards
Using PostHog MCP
When setting up analytics for a project:
1. First, check existing dashboards:
- Use `dashboards-get-all` to list current dashboards
2. Create project-appropriate dashboards:
- Use `dashboard-create` with descriptive name
3. Create insights for each dashboard:
- Use `query-run` to test queries
- Use `insight-create-from-query` to save
- Use `add-insight-to-dashboard` to organize
4. Set up key funnels:
- Signup funnel
- Onboarding funnel
- Purchase/conversion funnelDashboard Creation Workflow
// Example: Creating SaaS dashboards via MCP
// 1. Create dashboard
const dashboard = await mcp_posthog_dashboard_create({
name: "Activation Metrics",
description: "Track new user activation and onboarding",
tags: ["saas", "activation"],
});
// 2. Create insights
const signupFunnel = await mcp_posthog_query_run({
query: {
kind: "InsightVizNode",
source: {
kind: "FunnelsQuery",
series: [
{ kind: "EventsNode", event: "user_signed_up", name: "Signed Up" },
{ kind: "EventsNode", event: "onboarding_started", name: "Started Onboarding" },
{ kind: "EventsNode", event: "onboarding_completed", name: "Completed Onboarding" },
{ kind: "EventsNode", event: "first_value_action", name: "First Value" },
],
dateRange: { date_from: "-30d" },
},
},
});
// 3. Save and add to dashboard
const insight = await mcp_posthog_insight_create_from_query({
name: "Signup to Activation Funnel",
query: signupFunnel.query,
favorited: true,
});
await mcp_posthog_add_insight_to_dashboard({
insightId: insight.id,
dashboardId: dashboard.id,
});---
Privacy & Compliance
GDPR Compliance
// Opt-out handling
export function handleCookieConsent(consent: boolean) {
if (consent) {
posthog.opt_in_capturing();
} else {
posthog.opt_out_capturing();
}
}
// Check consent status
const hasConsent = posthog.has_opted_in_capturing();
// Initialize with consent check
posthog.init(key, {
opt_out_capturing_by_default: true, // Require explicit opt-in
respect_dnt: true, // Respect Do Not Track
});Data to Never Track
// ❌ NEVER track these
posthog.capture('event', {
password: '...', // Credentials
credit_card: '...', // Payment info
ssn: '...', // Government IDs
medical_info: '...', // Health data
full_address: '...', // Detailed location
});
// ✅ OK to track
posthog.capture('event', {
country: 'US', // General location
plan: 'pro', // Product info
feature_used: 'export', // Usage
});Property Sanitization
// lib/analytics.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'credit', 'ssn'];
function sanitizeProperties(props: Record<string, any>): Record<string, any> {
return Object.fromEntries(
Object.entries(props).filter(([key]) =>
!SENSITIVE_KEYS.some(sensitive => key.toLowerCase().includes(sensitive))
)
);
}
export function safeCapture(event: string, properties?: Record<string, any>) {
posthog.capture(event, sanitizeProperties(properties || {}));
}---
Testing Analytics
Development Mode
// Disable in development
if (process.env.NODE_ENV === 'development') {
posthog.opt_out_capturing();
// Or use debug mode
posthog.debug();
}E2E Testing
// playwright/fixtures.ts
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ page }, use) => {
// Mock PostHog to capture events
await page.addInitScript(() => {
window.capturedEvents = [];
window.posthog = {
capture: (event, props) => {
window.capturedEvents.push({ event, props });
},
identify: () => {},
reset: () => {},
};
});
await use(page);
},
});
// In tests
test('tracks signup event', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name=email]', 'test@example.com');
await page.click('button[type=submit]');
const events = await page.evaluate(() => window.capturedEvents);
expect(events).toContainEqual({
event: 'user_signed_up',
props: expect.objectContaining({ signup_method: 'email' }),
});
});---
Debugging
PostHog Toolbar
// Enable toolbar for debugging
posthog.init(key, {
// ...
loaded: (posthog) => {
if (process.env.NODE_ENV === 'development') {
posthog.debug();
// Toolbar available via PostHog dashboard
}
},
});Event Debugging
// Log all events in development
posthog.init(key, {
_onCapture: (eventName, eventData) => {
if (process.env.NODE_ENV === 'development') {
console.log('PostHog Event:', eventName, eventData);
}
},
});---
Quick Reference
Event Checklist by User Lifecycle
## Must-Track Events
### Acquisition
- [ ] `page_viewed` (automatic with capture_pageview)
- [ ] `user_signed_up`
- [ ] `user_logged_in`
### Activation
- [ ] `onboarding_started`
- [ ] `onboarding_step_completed`
- [ ] `onboarding_completed`
- [ ] `first_[key_action]` (your "aha moment")
### Engagement
- [ ] `[feature]_used`
- [ ] `[resource]_created`
- [ ] `search_performed`
- [ ] `invite_sent`
### Revenue
- [ ] `pricing_page_viewed`
- [ ] `checkout_started`
- [ ] `subscription_upgraded`
- [ ] `subscription_cancelled`
### Retention
- [ ] `session_started`
- [ ] `feature_[x]_used` (power features)Dashboard Templates
| Project Type | Key Dashboards |
|---|---|
| SaaS | Acquisition, Activation, Engagement, Retention, Revenue |
| E-Commerce | Conversion Funnel, Product Performance, Customer LTV |
| Content | Consumption, Engagement, Growth |
| AI/LLM | Usage, Quality, Cost |
| Mobile App | Installs, Onboarding, DAU/MAU, Crashes |
Properties to Always Include
// Auto-enriched by PostHog
$current_url
$browser
$device_type
$os
// Add these yourself
user_plan // 'free' | 'pro' | 'enterprise'
user_role // 'admin' | 'member'
company_id // For B2B
feature_context // Where in the appRelated skills
FAQ
What is the tracking philosophy?
Measure what matters, not everything: track events that inform decisions about activation, retention, funnels, and growth.
Which stacks are covered?
Next.js App Router, React (Vite/CRA), Python (FastAPI/Flask), and Node.js (Express/Hono).