PostHog feature flags, rollouts, A/B testing. Use when implementing gradual rollouts, A/B tests, kill switches, remote configuration, beta features, or user targeting with PostHog.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add agents-inc/skills --skill api-flags-posthog-flags --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Api Flags Posthog Flags?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/agents-inc-api-flags-posthog-flags-skills)More formats (shields.io, HTML) on the badges page.
---
name: api-flags-posthog-flags
description: PostHog feature flags, rollouts, A/B testing. Use when implementing gradual rollouts, A/B tests, kill switches, remote configuration, beta features, or user targeting with PostHog.
---
# Feature Flags with PostHog
> **Quick Guide:** Use PostHog feature flags for gradual rollouts, A/B testing, and remote configuration. Client-side: `useFeatureFlagEnabled` hook. Server-side: `posthog-node` with local evaluation. Always pair `useFeatureFlagPayload` with `useFeatureFlagEnabled` for experiments. Handle the `undefined` loading state on every flag check.
---
<critical_requirements>
## CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST always pair `useFeatureFlagPayload` with `useFeatureFlagEnabled` or `useFeatureFlagVariantKey` for experiments - payload hooks don't send exposure events)**
**(You MUST use the feature flags secure API key (phs\_\*) for server-side local evaluation - personal API keys are deprecated for this use)**
**(You MUST handle the `undefined` state when flags are loading - never assume a flag is immediately available)**
**(You MUST include flag owner and expiry date in flag metadata - flags without owners become orphaned debt)**
**(You MUST wrap flag usage in a single function when used in multiple places - prevents orphaned flag code on cleanup)**
</critical_requirements>
---
**Auto-detection:** PostHog feature flags, useFeatureFlagEnabled, useFeatureFlagPayload, useFeatureFlagVariantKey, PostHogFeature, isFeatureEnabled, getFeatureFlag, gradual rollout, A/B test, experiment, multivariate flag
**When to use:**
- Gradual rollouts (deploy to 10% users, then 50%, then 100%)
- A/B testing with experiments (measure impact of changes)
- Kill switches (instantly disable features without deploy)
- Remote configuration (change behavior without code changes)
- Beta features opt-in (let users try new features)
- User targeting (show features to specific cohorts)
**When NOT to use:**
- Simple on/off switches that never change (use environment variables)
- Configuration that must be compile-time (use build flags)
- Secrets or sensitive data (use secret management)
- Features that should always be on (just ship the code)
**Key patterns covered:**
- Client-side flag evaluation with React hooks
- Server-side local evaluation for performance
- Boolean vs multivariate flags
- Gradual rollouts with percentage targeting
- A/B testing and experiments
- Payloads for remote configuration
- Local development overrides
- Flag cleanup and lifecycle management
---
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Boolean flags, multivariate flags, PostHogFeature component, payloads, experiments, rollouts, lifecycle management
- [examples/server-side.md](examples/server-side.md) - Server-side evaluation, local evaluation setup, distributed environments
- [examples/development.md](examples/development.md) - Local overrides, bootstrapping, onFeatureFlags callback
- [reference.md](reference.md) - Decision frameworks and anti-patterns
---
<philosophy>
## Philosophy
Feature flags decouple deployment from release. You can ship code to production but control who sees it and when. This enables:
1. **Safe releases** - Roll out to 1% first, monitor, then expand
2. **Fast rollback** - Toggle off instantly without deploying
3. **Data-driven decisions** - A/B test to measure impact
4. **Progressive delivery** - Beta users first, then everyone
**Core principles:**
- Flags are temporary - plan for cleanup from day one
- Flags have owners - someone is responsible for each flag
- Simple flags are better - percentage rollouts over complex conditions
- Handle undefined - flags load asynchronously
**When to use feature flags:**
- Risky features that need gradual rollout
- Features requiring A/B testing for validation
- Features that may need instant rollback
- Beta programs with user opt-in
**When NOT to use feature flags:**
- Every feature (creates maintenance burden)
- Permanent configuration (use config files)
- Features that are ready for 100% release
</philosophy>
---
<patterns>
## Core Patterns
### Pattern 1: Client-Side Boolean Flags
Use `useFeatureFlagEnabled` for simple on/off features. Always handle the `undefined` loading state -- treating it as `false` causes a flash of wrong UI.
```typescript
const isNewCheckout = useFeatureFlagEnabled(FLAG_NEW_CHECKOUT);
if (isNewCheckout === undefined) return <Skeleton />; // Loading
if (isNewCheckout) return <NewCheckout />; // Enabled
return <LegacyCheckout />; // Disabled
```
Store flag keys as named constants in `lib/feature-flags.ts` to prevent typos and enable cleanup-by-grep.
See [examples/core.md](examples/core.md#pattern-1-client-side-boolean-flags) for full good/bad examples.
---
### Pattern 2: Multivariate Flags and Variants
Use `useFeatureFlagVariantKey` for A/B tests with multiple variants. Define variant constants alongside the flag key. Switch on variants with a default fallback to `control`.
```typescript
const variant = useFeatureFlagVariantKey(FLAG_PRICING_PAGE);
if (variant === undefined) return <Skeleton />;
switch (variant) {
case VARIANT_SIMPLE: return <SimplePricing />;
case VARIANT_DETAILED: return <DetailedPricing />;
default: return <ControlPricing />;
}
```
See [examples/core.md](examples/core.md#pattern-2-multivariate-flags-and-variants) for full example.
---
### Pattern 3: PostHogFeature Component
The `PostHogFeature` component provides automatic exposure tracking and built-in fallback handling with less boilerplate. Use `match={true}` for boolean flags or `match={VARIANT_KEY}` for specific variants.
```typescript
<PostHogFeature flag={FLAG_BETA} match={true} fallback={<Legacy />}>
<NewFeature />
</PostHogFeature>
```
See [examples/core.md](examples/core.md#pattern-3-posthogfeature-component) for boolean and variant examples.
---
### Pattern 4: Payloads for Remote Configuration
Use `useFeatureFlagPayload` for dynamic JSON configuration. **Always pair with `useFeatureFlagEnabled`** -- the payload hook alone does NOT send exposure events, breaking experiment tracking.
```typescript
const isEnabled = useFeatureFlagEnabled(FLAG_BANNER); // Sends exposure event
const payload = useFeatureFlagPayload(FLAG_BANNER); // Gets config
const config = payload ?? DEFAULT_BANNER_CONFIG;
```
See [examples/core.md](examples/core.md#pattern-4-feature-flag-payloads-for-remote-configuration) for full good/bad examples.
---
### Pattern 5: Server-Side Flag Evaluation
Use `posthog-node` with the Feature Flags Secure API Key (`phs_*`) for local evaluation. This reduces latency from ~500ms (network call) to ~10-50ms (local). The `personalApiKey` config option takes the `phs_*` key despite its legacy name.
```typescript
export const posthog = new PostHog(process.env.POSTHOG_API_KEY!, {
host: process.env.POSTHOG_HOST || "https://us.i.posthog.com",
personalApiKey: process.env.POSTHOG_FEATURE_FLAGS_KEY, // phs_* key
featureFlagsPollingInterval: POSTHOG_POLL_INTERVAL_MS, // default 30s
});
```
See [examples/server-side.md](examples/server-side.md) for API handler usage, local-only evaluation, and distributed/serverless environments.
---
### Pattern 6: Flag Lifecycle and Cleanup
Every flag needs an owner, a creation date, and an expected removal date. Wrap flag checks in a single helper function so cleanup is a one-file change.
```typescript
/**
* Owner: @john-doe | Created: 2025-01-15 | Remove by: 2025-02-15
*/
export const FLAG_NEW_CHECKOUT = "new-checkout-flow";
export function isNewCheckoutEnabled(flag: boolean | undefined): boolean {
return flag === true; // When removing: change to `return true;`
}
```
See [examples/core.md](examples/core.md#pattern-7-flag-cleanup-and-lifecycle-management) for full documentation patterns and stale flag detection.
</patterns>
---
<red_flags>
## RED FLAGS
**High Priority Issues:**
- Using `useFeatureFlagPayload` alone for experiments (no exposure tracking)
- Exposing Feature Flags Secure API key (`phs_*`) on client (security violation)
- No loading state handling (causes UI flash)
- Flags without owners or expiry dates (becomes permanent debt)
**Medium Priority Issues:**
- Magic string flag keys instead of constants (typos, hard to grep)
- Complex targeting rules on high-traffic flags (performance hit)
- Local evaluation in serverless/edge without external cache (cold start issues)
- Not using PostHog toolbar for local testing (harder debugging)
**Common Mistakes:**
- Checking flag in multiple places instead of wrapper function
- Not bootstrapping flags for SSR (content flash on hydration)
- Running experiments without defined primary metric
- Peeking at experiment results before completion
- Rolling out to 100% without cleanup plan
**Gotchas & Edge Cases:**
- PostHog uses deterministic hashing - same user always gets same variant
- Decreasing rollout percentage can remove users who were previously included
- Local evaluation requires Feature Flags Secure API Key (`phs_*`) - personal API keys are deprecated
- Flags load asynchronously - first render always has undefined
- GeoIP targeting uses server IP by default in posthog-node v3+
- Experiments need minimum 50 exposures per variant for results
- Stale flag = 100% rollout + not evaluated in 30 days
- `onFeatureFlags` callback receives three parameters: `flags`, `flagVariants`, `{ errorsLoading }` (third parameter)
- External cache providers (Redis, KV) are experimental - Node.js/Python SDKs only
</red_flags>
---
<critical_reminders>
## CRITICAL REMINDERS
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST always pair `useFeatureFlagPayload` with `useFeatureFlagEnabled` or `useFeatureFlagVariantKey` for experiments - payload hooks don't send exposure events)**
**(You MUST use the feature flags secure API key (phs\_\*) for server-side local evaluation - personal API keys are deprecated for this use)**
**(You MUST handle the `undefined` state when flags are loading - never assume a flag is immediately available)**
**(You MUST include flag owner and expiry date in flag metadata - flags without owners become orphaned debt)**
**(You MUST wrap flag usage in a single function when used in multiple places - prevents orphaned flag code on cleanup)**
**Failure to follow these rules will cause incorrect experiment results, security vulnerabilities, UI flashing, and technical debt.**
</critical_reminders>
---
## Sources
- [PostHog React Integration](https://posthog.com/docs/libraries/react)
- [PostHog Feature Flags](https://posthog.com/docs/feature-flags)
- [PostHog Feature Flag Best Practices](https://posthog.com/docs/feature-flags/best-practices)
- [PostHog Server-Side Local Evaluation](https://posthog.com/docs/feature-flags/local-evaluation)
- [PostHog Creating Feature Flags](https://posthog.com/docs/feature-flags/creating-feature-flags)
- [PostHog How to Do a Phased Rollout](https://posthog.com/tutorials/phased-rollout)
- [PostHog Feature Flag Testing](https://posthog.com/docs/feature-flags/testing)
- [PostHog Experiments](https://posthog.com/ab-testing)
- [PostHog Feature Flag Overrides](https://posthog.com/docs/toolbar/override-feature-flags)
- [Don't Make These Feature Flag Mistakes](https://posthog.com/newsletter/feature-flag-mistakes)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!