posthog-analytics
Skill708 starsChanged 5 months agoMITView on GitHub
PostHog analytics, event tracking, feature flags, dashboards
- Reads credentials
- Installs packages
7 other projects use a version of this.
Adopted by
Most recently taken up by
- kevinbadi/seo-agent-kittheir own version, first seen Oct 5, 2026
- lsongdev/skillstheir own version, first seen Sep 10, 2026
- SylphAI-Inc/skillstheir own version, first seen Aug 5, 2026
- alinaqi/maggyword for word, first seen Jun 12, 2026
- CodeAlive-AI/ceo-ai-ostheir own version, first seen May 18, 2026
- Ampli-Group/agentic-mobile-blueprinttheir own version, first seen May 11, 2026
Other versions7 projects use a version of this.
Word for word · 1
- alinaqi/maggy★ 711100% same
Edited · 0
None found.
Wrote their own version · 6
- SylphAI-Inc/skills★ 111same name
- CodeAlive-AI/ceo-ai-os★ 9same name
- kevinbadi/seo-agent-kit★ 9same name
- Ampli-Group/agentic-mobile-blueprint★ 4same name
- Layneformalized225/ai-cofounder★ 1same name
- lsongdev/skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository75 other files it gives its agents.
- templates/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- templates/CLAUDE.mdCLAUDE.md
- aeo-optimizationSkill
- agentic-developmentSkill
- agent-teamsSkill
- ai-modelsSkill
- android-javaSkill
- android-kotlinSkill
- autonomous-testingSkill
- aws-auroraSkill
- aws-dynamodbSkill
- azure-cosmosdbSkill
- baseSkill
- build-in-publicSkill
- cloudflare-d1Skill
- code-deduplicationSkill
- code-graphSkill
- code-reviewSkill
- codex-reviewSkill
- commit-hygieneSkill
- context-shuntSkill
- council-reviewSkill
- cpg-analysisSkill
- credentialsSkill
- cross-agent-delegationSkill
- database-schemaSkill
- dataforseoSkill
- demo-videoSkill
- existing-repoSkill
- external-model-delegationSkill
- firebaseSkill
- flutterSkill
- gemini-reviewSkill
- icpgSkill
- iterative-developmentSkill
- klaviyoSkill
- llm-patternsSkill
- maggySkill
- medusaSkill
- mnemosSkill
- model-routingSkill
- ms-teams-appsSkill
- nodejs-backendSkill
- playwright-testingSkill
- polyphonySkill
- project-toolingSkill
- pwa-developmentSkill
- pythonSkill
- react-nativeSkill
- react-webSkill
- reddit-adsSkill
- reddit-apiSkill
- security-auditSkill
- securitySkill
- session-managementSkill
- shopify-appsSkill
- site-architectureSkill
- supabase-nextjsSkill
- supabase-nodeSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
2 rules. 0 are common, 1 is distinctive, and 1 is very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 28
Don't track everything. Track what informs decisions.
- Very specific: line 220
Add to credentials.md patterns:
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
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 appDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

