Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
alinaqi avatar

Posthog Analytics

  • 760 installs
  • 706 repo stars
  • Updated July 14, 2026
  • alinaqi/claude-bootstrap

posthog-analytics is a Claude Code skill that correctly implements PostHog event tracking, user identification, feature flags, and targeted dashboards for developers who need product analytics wired into their applicatio

About

posthog-analytics is an alinaqi/claude-bootstrap skill for implementing product analytics with PostHog, drawing on official PostHog documentation for product analytics and feature flags. The skill follows a measure-what-matters philosophy—tracking events that answer specific product questions rather than logging everything. Developers reach for posthog-analytics when adding analytics, feature flags, or event tracking with PostHog SDKs and configuring project-specific dashboards. It covers user identification, event schema decisions, and feature-flag rollout patterns so analytics code ships correctly the first time. Medium effort and non-user-invocable by default, it is meant for agent-driven implementation during active product development.

  • Implements PostHog analytics, event tracking, feature flags, and project-specific dashboards
  • Follows philosophy of measuring only what matters: activation, retention, funnels, feature usage, acquisition
  • Provides Next.js App Router installation and initialization patterns
  • Handles person_profiles, manual pageview capture, and environment-specific configuration
  • Includes guidance for production-grade analytics that informs product decisions

Posthog Analytics by the numbers

  • 760 all-time installs (skills.sh)
  • Ranked #1,365 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alinaqi/claude-bootstrap --skill posthog-analytics

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs760
repo stars706
Security audit3 / 3 scanners passed
Last updatedJuly 14, 2026
Repositoryalinaqi/claude-bootstrap

How do you implement PostHog analytics and feature flags correctly?

Correctly implement PostHog event tracking, user identification, feature flags, and targeted dashboards in their product.

Who is it for?

Developers adding PostHog product analytics, feature flags, or event tracking to a SaaS app who want SDK-correct implementation.

Skip if: Teams using a different analytics platform or projects that need only marketing SEO tracking without in-app PostHog SDK work.

When should I use this skill?

The developer asks to add PostHog analytics, event tracking, user identification, feature flags, or PostHog dashboards.

What you get

PostHog event tracking code, user identify calls, feature-flag checks, and dashboard configuration for product questions.

  • Event tracking code
  • Feature flag integration
  • Dashboard setup

Files

SKILL.mdMarkdownGitHub ↗

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.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 TypeKey Dashboards
SaaSAcquisition, Activation, Engagement, Retention, Revenue
E-CommerceConversion Funnel, Product Performance, Customer LTV
ContentConsumption, Engagement, Growth
AI/LLMUsage, Quality, Cost
Mobile AppInstalls, 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

Related skills

How it compares

Pick this over generic analytics advice when the stack is PostHog and SDK-level event, identify, and flag implementation is required.

FAQ

What does the posthog-analytics skill implement?

posthog-analytics implements PostHog event tracking, user identification, feature flags, and project-specific dashboards following official PostHog documentation. The skill emphasizes measuring specific product questions instead of tracking every possible event.

When should developers invoke posthog-analytics?

posthog-analytics should be invoked when adding analytics, feature flags, or event tracking with PostHog during product development. Use it when SDK integration and dashboard setup must follow PostHog best practices rather than ad hoc logging.

Is Posthog Analytics safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.