Skip to content
OpenSmartRoute
Skillv1.0.0

posthog-sdk-patterns

Production-ready PostHog SDK patterns: singleton client, typed events, React hooks, Next.js App Router integration, and Python patterns. Trigger: "posthog SDK patterns", "posthog best practices", "pos

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (plugins/saas-packs/posthog-pack/skills/posthog-sdk-patterns/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill posthog-sdk-patterns. Copyright stays with the author (MIT).

PostHog SDK Patterns

Overview

Production-ready patterns for PostHog integrations: singleton client, type-safe event capture, React hooks for feature flags, Next.js App Router provider, and Python integration patterns.

Prerequisites

  • posthog-js and/or posthog-node installed
  • TypeScript project with strict mode
  • React/Next.js (for frontend patterns)

Instructions

Step 1: Singleton Server Client

// lib/posthog-server.ts
import { PostHog } from 'posthog-node';

let client: PostHog | null = null;

export function getPostHogServer(): PostHog {
  if (!client) {
    client = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
      personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY,
      flushAt: 20,
      flushInterval: 10000,
    });
  }
  return client;
}

// Graceful shutdown hook
process.on('SIGTERM', async () => {
  if (client) await client.shutdown();
  process.exit(0);
});

Step 2: Type-Safe Event Capture

// lib/analytics/events.ts
type EventMap = {
  user_signed_up: { method: 'email' | 'google' | 'github' };
  feature_used: { feature_name: string; duration_ms?: number };
  subscription_started: { plan: string; interval: 'monthly' | 'annual'; mrr: number };
  item_created: { item_type: string; source: 'web' | 'api' };
  search_performed: { query: string; results_count: number };
};

export function capture<K extends keyof EventMap>(
  distinctId: string,
  event: K,
  properties: EventMap[K]
) {
  const ph = getPostHogServer();
  ph.capture({ distinctId, event, properties });
}

// Usage: fully type-checked
capture('user-123', 'subscription_started', {
  plan: 'pro',
  interval: 'annual',
  mrr: 99,
});

Step 3: React Feature Flag Hook

// hooks/useFeatureFlag.ts
'use client';
import { useEffect, useState } from 'react';
import posthog from 'posthog-js';

export function useFeatureFlag(flagKey: string): boolean | undefined {
  const [enabled, setEnabled] = useState<boolean | undefined>(undefined);

  useEffect(() => {
    // Check immediately if flags are already loaded
    const current = posthog.isFeatureEnabled(flagKey);
    if (current !== undefined) {
      setEnabled(current);
    }

    // Listen for flag updates (initial load or remote changes)
    posthog.onFeatureFlags(() => {
      setEnabled(posthog.isFeatureEnabled(flagKey) ?? false);
    });
  }, [flagKey]);

  return enabled;
}

// Multivariate variant hook
export function useFeatureFlagVariant(flagKey: string): string | undefined {
  const [variant, setVariant] = useState<string | undefined>(undefined);

  useEffect(() => {
    posthog.onFeatureFlags(() => {
      const value = posthog.getFeatureFlag(flagKey);
      setVariant(typeof value === 'string' ? value : undefined);
    });
  }, [flagKey]);

  return variant;
}

// Usage in a component
function CheckoutButton() {
  const newCheckout = useFeatureFlag('new-checkout-v2');

  if (newCheckout === undefined) return <Skeleton />; // Loading
  return newCheckout ? <NewCheckout /> : <LegacyCheckout />;
}

Step 4: Next.js App Router Provider

// app/providers.tsx
'use client';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, Suspense } from 'react';

function PostHogPageView() {
  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 PHProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
      capture_pageview: false, // We handle manually in PostHogPageView
      capture_pageleave: true,
      loaded: (ph) => {
        if (process.env.NODE_ENV === 'development') ph.debug();
      },
    });
  }, []);

  return (
    <PostHogProvider client={posthog}>
      <Suspense fallback={null}>
        <PostHogPageView />
      </Suspense>
      {children}
    </PostHogProvider>
  );
}

// app/layout.tsx
// import { PHProvider } from './providers';
// export default function RootLayout({ children }) {
//   return <html><body><PHProvider>{children}</PHProvider></body></html>;
// }

Step 5: Reverse Proxy for Ad Blocker Bypass

// next.config.js — proxy PostHog through your domain
module.exports = {
  async rewrites() {
    return [
      { source: '/ingest/static/:path*', destination: 'https://us-assets.i.posthog.com/static/:path*' },
      { source: '/ingest/:path*', destination: 'https://us.i.posthog.com/:path*' },
      { source: '/ingest/decide', destination: 'https://us.i.posthog.com/decide' },
    ];
  },
};

// Then in your posthog.init:
// posthog.init('phc_...', { api_host: '/ingest' });

Step 6: Python Patterns

# analytics/posthog_client.py
import posthog
import os
import atexit
from functools import lru_cache

@lru_cache(maxsize=1)
def get_posthog():
    """Singleton PostHog client."""
    posthog.project_api_key = os.environ['POSTHOG_PROJECT_KEY']
    posthog.host = os.getenv('POSTHOG_HOST', 'https://us.i.posthog.com')
    posthog.personal_api_key = os.getenv('POSTHOG_PERSONAL_API_KEY')
    posthog.debug = os.getenv('ENVIRONMENT') == 'development'
    return posthog

# Ensure flush on exit
atexit.register(lambda: get_posthog().shutdown())

# Type-safe capture helper
def track(distinct_id: str, event: str, properties: dict | None = None):
    ph = get_posthog()
    ph.capture(distinct_id, event, properties or {})

# Feature flag check
def is_enabled(flag_key: str, distinct_id: str, default: bool = False) -> bool:
    ph = get_posthog()
    result = ph.feature_enabled(flag_key, distinct_id)
    return result if result is not None else default

Error Handling

Pattern Use Case Benefit
Singleton client All SDK usage Prevents multiple initializations
Type-safe events Event capture Compile-time property validation
Feature flag hooks React components Auto-updates on flag changes
Reverse proxy Production Bypasses ad blockers
Graceful shutdown Server processes No lost events on SIGTERM

Output

  • Singleton PostHog server client with shutdown hook
  • Type-safe event capture with TypeScript generics
  • React hooks for feature flags (boolean and multivariate)
  • Next.js App Router provider with pageview tracking
  • Reverse proxy configuration for ad blocker bypass

Resources

Next Steps

Apply patterns in posthog-core-workflow-a for real-world usage.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-posthog-sdk-patterns/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-posthog-sdk-patterns.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-posthog-sdk-patterns",
  "kind": "skill",
  "name": "posthog-sdk-patterns",
  "description": "Production-ready PostHog SDK patterns: singleton client, typed events, React hooks, Next.js App Router integration, and Python patterns. Trigger: \"posthog SDK patterns\", \"posthog best practices\", \"posthog React hook\", \"posthog Next.js\", \"posthog typescript\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "saas",
      "posthog",
      "python",
      "typescript",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Production-ready PostHog SDK patterns: singleton client, typed events, React hooks, Next.js App Router integration, and Python patterns. Trigger: \"posthog SDK patterns\", \"posthog best practices\", \"posthog React hook\", \"posthog Next.js\", \"posthog typescript\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "plugins/saas-packs/posthog-pack/skills/posthog-sdk-patterns/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/plugins/saas-packs/posthog-pack/skills/posthog-sdk-patterns/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/plugins/saas-packs/posthog-pack/skills/posthog-sdk-patterns/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit"
    ],
    "license": "MIT"
  },
  "instructions": "# PostHog SDK Patterns\n\n## Overview\n\nProduction-ready patterns for PostHog integrations: singleton client, type-safe event capture, React hooks for feature flags, Next.js App Router provider, and Python integration patterns.\n\n## Prerequisites\n\n- `posthog-js` and/or `posthog-node` installed\n- TypeScript project with strict mode\n- React/Next.js (for frontend patterns)\n\n## Instructions\n\n### Step 1: Singleton Server Client\n\n```typescript\n// lib/posthog-server.ts\nimport { PostHog } from 'posthog-node';\n\nlet client: PostHog | null = null;\n\nexport function getPostHogServer(): PostHog {\n  if (!client)",
  "cost": {
    "context_tokens": 1812
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-posthog-sdk-patterns/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.