Claude Code for PostHog: Product Analytics and Feature Flags — Claude Skills 360 Blog
Blog / Tooling / Claude Code for PostHog: Product Analytics and Feature Flags
Tooling

Claude Code for PostHog: Product Analytics and Feature Flags

Published: March 15, 2027
Read time: 7 min read
By: Claude Skills 360

PostHog is an open-source product analytics platform — self-hosted or cloud-hosted, with event tracking, session recordings, feature flags, A/B testing, and user surveys in one SDK. posthog.init(apiKey, { api_host }) initializes the client. posthog.capture("event_name", { properties }) tracks events. posthog.identify(userId, { email, name }) associates events to a user profile. posthog.isFeatureEnabled("flag-key") checks feature flags; useFeatureFlagEnabled is the React hook equivalent. posthog.getFeatureFlagPayload retrieves A/B test variant payloads. The posthog-node SDK tracks server-side events from API handlers. posthog.group associates events to organizations or accounts. Privacy controls include posthog.opt_out_capturing() and posthog.set_config({ disable_session_recording: true }). Claude Code generates PostHog initialization, event tracking, feature flag integrations, server-side tracking, and the React provider setup for product analytics in Next.js applications.

CLAUDE.md for PostHog

## PostHog Stack
- Version: posthog-js >= 1.170, posthog-node >= 4.0
- Init: posthog.init(API_KEY, { api_host: "https://app.posthog.com", autocapture: true })
- Events: posthog.capture("event", { prop: value }) — snake_case event names
- Identify: posthog.identify(userId, { email, name, plan }) — after auth
- Groups: posthog.group("company", companyId, { name, plan }) — org-level analytics
- Flags: const enabled = posthog.isFeatureEnabled("new-checkout") — sync check
- React: PHProvider wrapper + useFeatureFlagEnabled("flag") hook
- Server: posthogClient.capture({ distinctId, event, properties }) — posthog-node

Next.js Provider Setup

// providers/PostHogProvider.tsx — client provider
"use client"
import posthog from "posthog-js"
import { PostHogProvider } from "posthog-js/react"
import { useEffect } from "react"

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://app.posthog.com",

      // Capture configuration
      autocapture: true,  // Automatic click/change tracking
      capture_pageview: false,  // Handle manually for Next.js SPA navigation
      capture_pageleave: true,

      // Session recording
      session_recording: {
        maskAllInputs: true,   // Privacy: mask all form inputs
        maskAllText: false,
        recordCrossOriginIframes: false,
      },

      // Privacy controls
      opt_out_capturing_by_default: false,
      persistence: "localStorage+cookie",

      // Bootstrap flags for instant access (avoids UI flicker)
      // bootstrap: { featureFlags: serverSideFlags },

      loaded: (ph) => {
        if (process.env.NODE_ENV === "development") {
          ph.debug()  // Log events in dev mode
        }
      },
    })
  }, [])

  return <PostHogProvider client={posthog}>{children}</PostHogProvider>
}
// app/layout.tsx — include provider with page tracking
import { PHProvider } from "@/providers/PostHogProvider"
import { PageViewTracker } from "@/components/analytics/PageViewTracker"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <PHProvider>
          <PageViewTracker />
          {children}
        </PHProvider>
      </body>
    </html>
  )
}
// components/analytics/PageViewTracker.tsx — track Next.js route changes
"use client"
import { usePathname, useSearchParams } from "next/navigation"
import { usePostHog } from "posthog-js/react"
import { Suspense, useEffect } from "react"

function Tracker() {
  const pathname = usePathname()
  const searchParams = useSearchParams()
  const posthog = usePostHog()

  useEffect(() => {
    if (posthog) {
      posthog.capture("$pageview", {
        $current_url: window.location.href,
      })
    }
  }, [pathname, searchParams, posthog])

  return null
}

export function PageViewTracker() {
  return (
    <Suspense>
      <Tracker />
    </Suspense>
  )
}

Event Tracking

// lib/analytics.ts — typed event tracking utilities
import { usePostHog } from "posthog-js/react"
import { useCallback } from "react"

// Type-safe event definitions
type AnalyticsEvent =
  | { event: "order_placed"; properties: { orderId: string; totalCents: number; itemCount: number } }
  | { event: "order_cancelled"; properties: { orderId: string; reason?: string } }
  | { event: "checkout_started"; properties: { cartTotal: number; itemCount: number } }
  | { event: "checkout_abandoned"; properties: { step: "shipping" | "payment"; cartTotal: number } }
  | { event: "product_viewed"; properties: { productId: string; productName: string; priceCents: number } }
  | { event: "feature_flag_evaluated"; properties: { flag: string; variant: string | boolean } }

export function useAnalytics() {
  const posthog = usePostHog()

  const track = useCallback(
    ({ event, properties }: AnalyticsEvent) => {
      posthog?.capture(event, properties)
    },
    [posthog]
  )

  const identifyUser = useCallback(
    (userId: string, traits: { email: string; name: string; plan?: string }) => {
      posthog?.identify(userId, traits)
    },
    [posthog]
  )

  const trackGroup = useCallback(
    (companyId: string, traits: { name: string; plan: string; memberCount: number }) => {
      posthog?.group("company", companyId, traits)
    },
    [posthog]
  )

  return { track, identifyUser, trackGroup }
}

// Usage in component
export function CheckoutButton({ cart }: { cart: Cart }) {
  const { track } = useAnalytics()

  return (
    <button
      onClick={() => {
        track({
          event: "checkout_started",
          properties: { cartTotal: cart.totalCents, itemCount: cart.items.length },
        })
        router.push("/checkout")
      }}
    >
      Checkout
    </button>
  )
}

Feature Flags

// hooks/useFeatureFlags.ts — typed feature flag hooks
import { useFeatureFlagEnabled, useFeatureFlagPayload, usePostHog } from "posthog-js/react"

// Typed flags
export function useNewCheckout() {
  return useFeatureFlagEnabled("new-checkout-flow")
}

export function usePricingVariant(): "control" | "annual_emphasis" | "monthly_emphasis" {
  const payload = useFeatureFlagPayload("pricing-page-test")
  return (payload as any)?.variant ?? "control"
}

// Component using flags
export function CheckoutFlow() {
  const useNewFlow = useNewCheckout()

  if (useNewFlow === undefined) {
    return <CheckoutSkeleton />  // Flags loading — avoid flicker
  }

  return useNewFlow ? <NewCheckoutFlow /> : <LegacyCheckoutFlow />
}

export function PricingPage() {
  const variant = usePricingVariant()
  const { track } = useAnalytics()

  useEffect(() => {
    track({ event: "feature_flag_evaluated", properties: { flag: "pricing-page-test", variant } })
  }, [variant])

  return (
    <div>
      {variant === "annual_emphasis" && <AnnualPricingHero />}
      {variant === "monthly_emphasis" && <MonthlyPricingHero />}
      {variant === "control" && <DefaultPricingHero />}
    </div>
  )
}

Server-Side Tracking

// lib/posthog-server.ts — server-side PostHog client
import { PostHog } from "posthog-node"

export const posthogServer = new PostHog(
  process.env.NEXT_PUBLIC_POSTHOG_KEY!,
  {
    host: process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "https://app.posthog.com",
    flushAt: 20,
    flushInterval: 10_000,
  }
)

// Flush on serverless function shutdown
if (typeof process !== "undefined") {
  process.on("beforeExit", async () => {
    await posthogServer.shutdown()
  })
}
// app/api/orders/route.ts — server-side event tracking
import { posthogServer } from "@/lib/posthog-server"
import { auth } from "@/lib/auth"

export async function POST(req: Request) {
  const session = await auth()
  const body = await req.json()

  const order = await createOrder(session!.user!.id, body)

  // Server-side event: more reliable than client for revenue tracking
  posthogServer.capture({
    distinctId: session!.user!.id,
    event: "order_placed",
    properties: {
      orderId: order.id,
      totalCents: order.totalCents,
      itemCount: order.items.length,
      $groups: { company: session!.user!.companyId },
    },
  })

  // Check feature flag server-side (for server-rendered content)
  const showUpsell = await posthogServer.isFeatureEnabled("post-order-upsell", session!.user!.id)

  return Response.json({ order, showUpsell })
}

For the Mixpanel analytics alternative when funnel analysis, advanced cohort analysis, and impact analysis on top of event tracking are needed beyond PostHog’s feature set, see the analytics integration patterns. For the Segment CDP alternative that acts as a data routing layer — sending events to multiple analytics platforms (PostHog, Mixpanel, Amplitude) from a single SDK without vendor lock-in, the OpenTelemetry guide covers observability pipeline architecture. The Claude Skills 360 bundle includes PostHog skill sets covering event tracking, feature flags, and server-side analytics. Start with the free tier to try analytics integration generation.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free