Configure PostHog across development, staging, and production environments. Use when setting up multi-environment event capture, managing separate PostHog projects per environment, or configuring feature flags and recordings by env. Trigger with phrases like "posthog environments", "posthog staging", "posthog dev prod", "posthog environment setup", "posthog project per env".
From posthog-packnpx claudepluginhub nickloveinvesting/nick-love-plugins --plugin posthog-packThis skill is limited to using the following tools:
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
Separate PostHog projects per environment is strongly recommended over using one project with event filtering. Each environment gets its own Project API Key (starts with phc_) and Project ID.
phc_...) and Project IDs for each environment| Environment | PostHog Project | Session Recording | Autocapture | Key Source |
|---|---|---|---|---|
| Development | myapp-dev | Disabled | Enabled | .env.local |
| Staging | myapp-staging | Disabled | Enabled | CI/CD secrets |
| Production | myapp-production | Enabled (sampled) | Enabled | Secret manager |
PostHog Cloud: app.posthog.com > New Project
- "myapp-development" -> copy phc_... API key
- "myapp-staging" -> copy phc_... API key
- "myapp-production" -> copy phc_... API key
// config/posthog.ts
type Env = "development" | "staging" | "production";
interface PostHogConfig {
apiKey: string; // phc_... project key
host: string; // app.posthog.com or self-hosted URL
sessionRecording: boolean;
samplingRate: number; // 0-1 for session recording
}
const configs: Record<Env, PostHogConfig> = {
development: {
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY_DEV!,
host: "https://app.posthog.com",
sessionRecording: false, // never record in dev
samplingRate: 0,
},
staging: {
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY_STAGING!,
host: "https://app.posthog.com",
sessionRecording: false, // no recordings in staging
samplingRate: 0,
},
production: {
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY_PROD!,
host: "https://app.posthog.com",
sessionRecording: true,
samplingRate: 0.25, // record 25% of sessions
},
};
export function getPostHogConfig(): PostHogConfig {
const env = (process.env.NODE_ENV || "development") as Env;
const config = configs[env] || configs.development;
if (!config.apiKey) {
console.warn(`PostHog API key not set for ${env} -- analytics disabled`);
}
return config;
}
// app/providers.tsx
"use client";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect } from "react";
import { getPostHogConfig } from "../config/posthog";
export function PHProvider({ children }: { children: React.ReactNode }) {
const config = getPostHogConfig();
useEffect(() => {
if (!config.apiKey) return;
posthog.init(config.apiKey, {
api_host: config.host,
disable_session_recording: !config.sessionRecording,
session_recording: config.sessionRecording
? { sampleRate: config.samplingRate }
: undefined,
capture_pageview: false, // use usePathname in Next.js App Router
loaded: (ph) => {
if (process.env.NODE_ENV === "development") {
ph.debug();
}
},
});
}, []);
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}
// lib/posthog-server.ts
import { PostHog } from "posthog-node";
let _client: PostHog | null = null;
export function getPostHogServer(): PostHog {
if (_client) return _client;
const config = getPostHogConfig();
if (!config.apiKey) return { capture: () => {} } as any; // no-op if unconfigured
_client = new PostHog(config.apiKey, {
host: config.host,
flushAt: 20,
flushInterval: 10000, # 10000: 10 seconds in ms
});
return _client;
}
# .env.local
NEXT_PUBLIC_POSTHOG_KEY_DEV=phc_dev_abc123
POSTHOG_PROJECT_ID_DEV=12345 # port 12345 - example/test
# .env.staging
NEXT_PUBLIC_POSTHOG_KEY_STAGING=phc_staging_def456
POSTHOG_PROJECT_ID_STAGING=12346 # 12346 = configured value
# Production (GitHub Actions / cloud secret manager)
# NEXT_PUBLIC_POSTHOG_KEY_PROD=phc_prod_xyz789
# POSTHOG_PROJECT_ID_PROD=12347
| Issue | Cause | Solution |
|---|---|---|
| Dev events in prod project | Same API key across envs | Use separate projects per env |
| No events captured | apiKey not set in env vars | Check NEXT_PUBLIC_ prefix for client-side keys |
| Session recordings in staging | sessionRecording: true in staging | Set sessionRecording: false for non-prod |
401 from server-side | Wrong key type | Project key (phc_...) is for capture; personal key for admin API |
import { getPostHogConfig } from "./config/posthog";
const cfg = getPostHogConfig();
console.log(`PostHog key: ${cfg.apiKey.slice(0, 10)}...`);
console.log(`Session recording: ${cfg.sessionRecording ? "ON" : "OFF"}`);
// Staging project: 100% rollout for QA
// Production project: 10% initial rollout
const flagValue = await posthogServer.getFeatureFlag("new-checkout", userId);
For webhook setup, see posthog-webhooks-events.