posthog-analytics

PostHog Analytics Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "posthog-analytics" with this command: npx skills add manojbajaj95/gtm-skills/manojbajaj95-gtm-skills-posthog-analytics

PostHog Analytics Skill

Load with: base.md + [framework].md

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.com

Add 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 tense

Core 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 source

E-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 distribution

Content/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 sources

AI/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 funnel

Dashboard 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 app

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

youtube-video-hook

No summary provided by upstream source.

Repository SourceNeeds Review
General

growth-strategy

No summary provided by upstream source.

Repository SourceNeeds Review
General

youtube-video-analyst

No summary provided by upstream source.

Repository SourceNeeds Review
General

writing-linkedin-posts

No summary provided by upstream source.

Repository SourceNeeds Review