or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/posthog-js@1.335.x

docs

index.md
tile.json

tessl/npm-posthog-js

tessl install tessl/npm-posthog-js@1.335.0

PostHog Browser JS Library is a comprehensive browser analytics and feature management SDK that enables developers to capture user events, track product analytics, manage feature flags, record session replays, and implement feedback mechanisms like surveys and conversations in web applications.

feature-flags.mddocs/reference/

Feature Flags

Feature flags allow you to control feature rollouts, run A/B tests, and manage feature access dynamically without deploying new code.

Capabilities

Get Feature Flag Value

Gets the value of a feature flag (boolean, string, or undefined).

/**
 * Gets the value of a feature flag
 * @param key - Feature flag key
 * @param options - Options for flag evaluation
 * @returns Flag value (boolean for boolean flags, string for multivariate flags, undefined if not found)
 */
function getFeatureFlag(
    key: string,
    options?: { send_event?: boolean }
): boolean | string | undefined;

Usage Examples:

// Boolean flag
const newFeatureEnabled = posthog.getFeatureFlag('new-dashboard');
if (newFeatureEnabled) {
    showNewDashboard();
}

// Multivariate flag
const buttonColor = posthog.getFeatureFlag('button-color-test');
if (buttonColor === 'blue') {
    applyBlueButton();
} else if (buttonColor === 'green') {
    applyGreenButton();
}

// Suppress $feature_flag_called event
const flag = posthog.getFeatureFlag('silent-flag', { send_event: false });

Check If Feature Is Enabled

Checks if a boolean feature flag is enabled.

/**
 * Checks if a feature flag is enabled (boolean flags only)
 * @param key - Feature flag key
 * @param options - Options for flag evaluation
 * @returns True if enabled, false if disabled, undefined if not found or not boolean
 */
function isFeatureEnabled(
    key: string,
    options?: { send_event: boolean }
): boolean | undefined;

Usage Examples:

// Simple boolean check
if (posthog.isFeatureEnabled('new-checkout')) {
    renderNewCheckout();
} else {
    renderOldCheckout();
}

// With default fallback
const showBeta = posthog.isFeatureEnabled('beta-features') ?? false;

// Suppress event tracking
const flag = posthog.isFeatureEnabled('feature', { send_event: false });

Get Feature Flag Payload

Gets the JSON payload associated with a feature flag.

/**
 * Gets the payload associated with a feature flag
 * @param key - Feature flag key
 * @returns JSON payload or undefined if not found
 */
function getFeatureFlagPayload(key: string): JsonType;

Usage Examples:

// Get configuration from flag payload
const config = posthog.getFeatureFlagPayload('experiment-config');
if (config) {
    applyConfig({
        variant: config.variant,
        settings: config.settings
    });
}

// Get complex data structures
const pricing = posthog.getFeatureFlagPayload('pricing-experiment');
if (pricing) {
    setPrices({
        monthly: pricing.monthly_price,
        annual: pricing.annual_price,
        discount: pricing.discount_percent
    });
}

// Type-safe payload handling
interface PricingConfig {
    monthly_price: number;
    annual_price: number;
    discount_percent: number;
}

const typedPricing = posthog.getFeatureFlagPayload('pricing-experiment') as PricingConfig | undefined;
if (typedPricing) {
    setPrices(typedPricing);
}

Reload Feature Flags

Manually reloads all feature flags from the server.

/**
 * Manually reloads feature flags from the server
 * Useful after identify() or group() calls to get updated flags
 */
function reloadFeatureFlags(): void;

Usage Examples:

// Reload after identification
posthog.identify('user-123', { plan: 'enterprise' });
posthog.reloadFeatureFlags();

// Reload after group change
posthog.group('company', 'acme-inc');
posthog.reloadFeatureFlags();

// Force refresh
function refreshFeatures() {
    posthog.reloadFeatureFlags();
}

Update Feature Flags Locally

Updates feature flags locally for testing or overrides.

/**
 * Updates feature flags locally (for testing/overrides)
 * @param flags - Object mapping flag keys to values
 * @param payloads - Object mapping flag keys to payloads (optional)
 * @param options - Options for update behavior
 */
function updateFlags(
    flags: Record<string, boolean | string>,
    payloads?: Record<string, JsonType>,
    options?: { merge?: boolean }
): void;

Usage Examples:

// Override flags for testing
posthog.updateFlags({
    'new-feature': true,
    'experiment-variant': 'control'
});

// Override with payloads
posthog.updateFlags(
    { 'config-flag': true },
    { 'config-flag': { timeout: 5000, retries: 3 } }
);

// Merge with existing flags (default: replace)
posthog.updateFlags(
    { 'additional-flag': true },
    undefined,
    { merge: true }
);

Feature Flags Callback

Registers a callback that fires when feature flags are loaded or updated.

/**
 * Registers a callback for when feature flags are loaded
 * @param callback - Function to call when flags are loaded
 * @returns Function to unsubscribe from callback
 */
function onFeatureFlags(callback: FeatureFlagsCallback): () => void;

Usage Examples:

// Listen for flag changes
const unsubscribe = posthog.onFeatureFlags((flags, variants, context) => {
    console.log('Flags loaded:', flags);
    console.log('Variants:', variants);

    if (context?.errorsLoading) {
        console.error('Error loading flags:', context.errorsLoading);
    }

    // Update UI based on flags
    updateFeatureUI(flags);
});

// Later, stop listening
unsubscribe();

// One-time callback
posthog.onFeatureFlags((flags) => {
    initializeApp(flags);
})();

Set Person Properties for Flags

Sets person property overrides for local feature flag evaluation.

/**
 * Sets person property overrides for local flag evaluation
 * @param properties - Person properties to use for flag evaluation
 * @param reloadFeatureFlags - Whether to reload flags after setting (default: true)
 */
function setPersonPropertiesForFlags(
    properties: Properties,
    reloadFeatureFlags?: boolean
): void;

Usage Examples:

// Override properties for flag evaluation
posthog.setPersonPropertiesForFlags({
    email: 'test@example.com',
    plan: 'enterprise'
});

// Set without reloading flags
posthog.setPersonPropertiesForFlags(
    { role: 'admin' },
    false
);

Reset Person Properties for Flags

Clears person property overrides for flag evaluation.

/**
 * Clears person property overrides for flags
 */
function resetPersonPropertiesForFlags(): void;

Usage Example:

// Clear overrides
posthog.resetPersonPropertiesForFlags();

Set Group Properties for Flags

Sets group property overrides for local feature flag evaluation.

/**
 * Sets group property overrides for local flag evaluation
 * @param properties - Object mapping group types to properties
 * @param reloadFeatureFlags - Whether to reload flags after setting (default: true)
 */
function setGroupPropertiesForFlags(
    properties: { [type: string]: Properties },
    reloadFeatureFlags?: boolean
): void;

Usage Examples:

// Override group properties
posthog.setGroupPropertiesForFlags({
    company: { plan: 'enterprise', seats: 100 },
    team: { role: 'engineering' }
});

// Set without reloading
posthog.setGroupPropertiesForFlags(
    { company: { industry: 'tech' } },
    false
);

Reset Group Properties for Flags

Clears group property overrides for flag evaluation.

/**
 * Clears group property overrides for flags
 * @param group_type - Optional group type to clear (clears all if not specified)
 */
function resetGroupPropertiesForFlags(group_type?: string): void;

Usage Examples:

// Clear all group overrides
posthog.resetGroupPropertiesForFlags();

// Clear specific group type
posthog.resetGroupPropertiesForFlags('company');

Early Access Features

Get Early Access Features

Gets available early access features for the current user.

/**
 * Gets available early access features
 * @param callback - Function to call with early access features
 * @param force_reload - Force reload from server (default: false)
 * @param stages - Filter by specific stages (optional)
 */
function getEarlyAccessFeatures(
    callback: EarlyAccessFeatureCallback,
    force_reload?: boolean,
    stages?: EarlyAccessFeatureStage[]
): void;

Usage Examples:

// Get all early access features
posthog.getEarlyAccessFeatures((features, context) => {
    console.log('Available features:', features);

    if (context?.error) {
        console.error('Error loading features:', context.error);
    }

    features.forEach(feature => {
        console.log(`${feature.name} (${feature.stage})`);
    });
});

// Force reload from server
posthog.getEarlyAccessFeatures((features) => {
    displayEarlyAccessUI(features);
}, true);

// Filter by specific stages
posthog.getEarlyAccessFeatures(
    (features) => {
        // Only beta features
        features.forEach(f => console.log(f.name));
    },
    false,
    [EarlyAccessFeatureStage.Beta]
);

// Filter multiple stages
posthog.getEarlyAccessFeatures(
    (features) => {
        // Alpha and beta features
        displayFeatureList(features);
    },
    false,
    [EarlyAccessFeatureStage.Alpha, EarlyAccessFeatureStage.Beta]
);

Update Early Access Feature Enrollment

Updates enrollment status for an early access feature.

/**
 * Updates enrollment status for an early access feature
 * @param key - Early access feature key
 * @param isEnrolled - Whether user is enrolled
 * @param stage - Feature stage (optional)
 */
function updateEarlyAccessFeatureEnrollment(
    key: string,
    isEnrolled: boolean,
    stage?: string
): void;

Usage Examples:

// Enroll user in beta feature
posthog.updateEarlyAccessFeatureEnrollment('new-editor', true, 'beta');

// Unenroll from feature
posthog.updateEarlyAccessFeatureEnrollment('new-editor', false);

// Enroll without stage
posthog.updateEarlyAccessFeatureEnrollment('feature-x', true);

Types

FeatureFlagsCallback

/**
 * Callback invoked when feature flags are loaded
 */
type FeatureFlagsCallback = (
    flags: Record<string, boolean | string>,
    variants: Record<string, string | undefined>,
    context?: { errorsLoading?: string }
) => void;

FeatureFlagDetail

/**
 * Detailed information about a feature flag
 */
interface FeatureFlagDetail {
    /**
     * Flag key
     */
    key: string;

    /**
     * Whether the flag is enabled
     */
    enabled: boolean;

    /**
     * Variant value for multivariate flags
     */
    variant?: string;

    /**
     * Reason for the flag evaluation result
     */
    reason?: EvaluationReason;

    /**
     * Additional metadata about the flag
     */
    metadata?: {
        /**
         * Flag ID
         */
        id?: number;

        /**
         * Flag version
         */
        version?: number;

        /**
         * Flag description
         */
        description?: string;

        /**
         * Flag payload
         */
        payload?: JsonType;
    };
}

EvaluationReason

/**
 * Reason for feature flag evaluation result
 */
type EvaluationReason =
    | 'local_evaluation'
    | 'remote_evaluation'
    | 'override'
    | 'bootstrap'
    | 'fallback'
    | 'disabled'
    | 'error';

EarlyAccessFeature

/**
 * Early access feature information
 */
interface EarlyAccessFeature {
    /**
     * Unique identifier
     */
    id: string;

    /**
     * Feature key
     */
    key: string;

    /**
     * Feature name
     */
    name: string;

    /**
     * Feature description
     */
    description?: string;

    /**
     * Current stage
     */
    stage: EarlyAccessFeatureStage;
}

EarlyAccessFeatureStage

/**
 * Early access feature stage
 */
enum EarlyAccessFeatureStage {
    Development = 'development',
    Alpha = 'alpha',
    Beta = 'beta',
    GeneralAvailability = 'general-availability',
}

EarlyAccessFeatureCallback

/**
 * Callback for early access feature loading
 */
type EarlyAccessFeatureCallback = (
    features: EarlyAccessFeature[],
    context?: { isLoaded: boolean; error?: string }
) => void;

JsonType

/**
 * JSON-serializable value
 */
type JsonType = any;

Properties

/**
 * Object containing properties
 */
type Properties = Record<string, Property | Property[]>;

Property

/**
 * A single property value
 */
type Property = string | number | boolean | null | undefined;

Deprecated Properties

decideEndpointWasHit

Deprecated: Use other flag status checking methods instead.

/**
 * @deprecated Use flagsEndpointWasHit instead
 * Checks if the decide endpoint (flags endpoint) has been hit
 * @returns True if endpoint was hit, false otherwise
 */
get decideEndpointWasHit(): boolean;

Usage Example:

// Check if flags endpoint was called
const wasHit = posthog.decideEndpointWasHit;
console.log('Flags endpoint hit:', wasHit);

Configuration

Feature Flag Settings

Configure feature flag behavior during initialization:

// Disable feature flags entirely
posthog.init('token', {
    advanced_disable_flags: true
});

// Disable flags on first load (load them later)
posthog.init('token', {
    advanced_disable_feature_flags_on_first_load: true
});

// Set timeout for flag requests
posthog.init('token', {
    feature_flag_request_timeout_ms: 5000 // 5 seconds
});

// Use separate API host for flags
posthog.init('token', {
    flags_api_host: 'https://flags.example.com'
});

// Bootstrap flags (skip initial load)
posthog.init('token', {
    bootstrap: {
        featureFlags: {
            'new-feature': true,
            'variant-test': 'control'
        },
        featureFlagPayloads: {
            'new-feature': { config: 'value' }
        }
    }
});

Best Practices

Defensive Flag Checks

Always handle undefined flags gracefully:

// ✅ Good: Handle undefined with default
const showNewUI = posthog.isFeatureEnabled('new-ui') ?? false;
if (showNewUI) {
    renderNewUI();
} else {
    renderOldUI();
}

// ✅ Good: Explicit undefined check
const flag = posthog.getFeatureFlag('experiment');
if (flag === undefined) {
    // Flags not loaded yet or flag doesn't exist
    showLoadingState();
} else if (flag) {
    showExperiment();
}

// ❌ Bad: Assumes flag exists
if (posthog.isFeatureEnabled('new-ui')) {
    renderNewUI();
}
// If flag is undefined, old UI won't render either

Wait for Flags to Load

Use callbacks to ensure flags are loaded:

// ✅ Good: Wait for flags
posthog.onFeatureFlags((flags) => {
    if (flags['critical-feature']) {
        initializeCriticalFeature();
    }
});

// ✅ Good: Check if loaded
function checkFeature() {
    const flag = posthog.getFeatureFlag('feature');
    if (flag !== undefined) {
        return flag;
    }
    // Wait for flags to load
    posthog.onFeatureFlags(() => {
        checkFeature();
    });
}

// ❌ Bad: Check immediately (may not be loaded)
if (posthog.isFeatureEnabled('feature')) {
    // This may not work if flags haven't loaded yet
}

Reload After Identity Changes

Reload flags after identifying users or changing groups:

// ✅ Good: Reload after identify
posthog.identify('user-123', { plan: 'enterprise' });
posthog.reloadFeatureFlags();

// ✅ Good: Reload after group change
posthog.group('company', 'acme-inc', { plan: 'enterprise' });
posthog.reloadFeatureFlags();

// ❌ Bad: Don't reload (flags may be stale)
posthog.identify('user-123');
// Flags still evaluated for anonymous user

Use Payloads for Configuration

Store configuration data in flag payloads:

// Flag payload in PostHog dashboard
{
    "timeout_ms": 5000,
    "max_retries": 3,
    "endpoint": "https://api.example.com/v2"
}

// Application code
const config = posthog.getFeatureFlagPayload('api-config');
if (config) {
    apiClient.configure({
        timeout: config.timeout_ms,
        retries: config.max_retries,
        endpoint: config.endpoint
    });
}

Testing with Flag Overrides

Override flags for local development and testing:

// Development mode overrides
if (process.env.NODE_ENV === 'development') {
    posthog.updateFlags({
        'new-feature': true,
        'debug-mode': true,
        'experiment-variant': 'test'
    });
}

// Testing specific scenarios
function testCheckoutFlow() {
    posthog.updateFlags({
        'new-checkout': true,
        'payment-provider': 'stripe-test'
    });

    // Run tests...

    // Reset after tests
    posthog.reloadFeatureFlags();
}

// URL-based overrides
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('feature_flags')) {
    const flags = JSON.parse(urlParams.get('feature_flags'));
    posthog.updateFlags(flags);
}

Advanced Feature Flags Sub-API

The posthog.featureFlags namespace provides additional methods for advanced feature flag management and introspection.

Get All Flag Keys

Returns an array of all feature flag keys that have been loaded.

/**
 * Gets all feature flag keys
 * @returns Array of flag keys
 */
posthog.featureFlags.getFlags(): string[];

Usage Example:

// Get all available flag keys
const flagKeys = posthog.featureFlags.getFlags();
console.log('Available flags:', flagKeys);

// Check if a specific flag exists
if (flagKeys.includes('new-feature')) {
    console.log('new-feature flag is available');
}

Get Flags With Details

Returns detailed information about all feature flags including evaluation metadata.

/**
 * Gets all flags with evaluation details
 * @returns Object mapping flag keys to FeatureFlagDetail objects
 */
posthog.featureFlags.getFlagsWithDetails(): Record<string, FeatureFlagDetail>;

interface FeatureFlagDetail {
    value: boolean | string;
    reason?: EvaluationReason;
    metadata?: FeatureFlagMetadata;
}

type EvaluationReason =
    | 'targeting_match'
    | 'condition_match'
    | 'rollout'
    | 'bootstrap'
    | 'override'
    | 'unknown';

interface FeatureFlagMetadata {
    evaluatedLocally?: boolean;
    flagType?: 'boolean' | 'multivariate';
}

Usage Example:

// Get detailed flag information
const details = posthog.featureFlags.getFlagsWithDetails();

Object.entries(details).forEach(([key, detail]) => {
    console.log(`Flag: ${key}`);
    console.log(`  Value: ${detail.value}`);
    console.log(`  Reason: ${detail.reason}`);
    console.log(`  Evaluated locally: ${detail.metadata?.evaluatedLocally}`);
});

Get All Flag Variants

Returns the variant values for all loaded feature flags.

/**
 * Gets all flag variants
 * @returns Object mapping flag keys to variant values
 */
posthog.featureFlags.getFlagVariants(): Record<string, string | boolean>;

Usage Example:

// Get all flag variants
const variants = posthog.featureFlags.getFlagVariants();
console.log('Flag variants:', variants);

// Use for analytics or debugging
posthog.capture('page_viewed', {
    active_variants: variants
});

Get All Flag Payloads

Returns all feature flag payloads in a single object.

/**
 * Gets all flag payloads
 * @returns Object mapping flag keys to payload values
 */
posthog.featureFlags.getFlagPayloads(): Record<string, JsonType>;

Usage Example:

// Get all payloads at once
const payloads = posthog.featureFlags.getFlagPayloads();

// Apply multiple configurations
Object.entries(payloads).forEach(([key, payload]) => {
    if (payload) {
        applyFeatureConfig(key, payload);
    }
});

Ensure Flags Loaded

Ensures that feature flags have been loaded from the server before proceeding. Useful for preventing race conditions.

/**
 * Ensures feature flags are loaded before proceeding
 * Blocks until flags are loaded or timeout occurs
 */
posthog.featureFlags.ensureFlagsLoaded(): void;

Usage Example:

// Wait for flags before critical initialization
async function initializeApp() {
    // Ensure flags are loaded first
    posthog.featureFlags.ensureFlagsLoaded();

    // Now safely check flags
    if (posthog.isFeatureEnabled('new-ui')) {
        loadNewUI();
    } else {
        loadLegacyUI();
    }
}

Set Anonymous Distinct ID

Sets the anonymous distinct ID used for feature flag evaluation. Useful for consistent flag evaluation before user identification.

/**
 * Sets the anonymous distinct ID for flag evaluation
 * @param anon_distinct_id - Anonymous distinct ID to use
 */
posthog.featureFlags.setAnonymousDistinctId(anon_distinct_id: string): void;

Usage Example:

// Set a custom anonymous ID
posthog.featureFlags.setAnonymousDistinctId('custom-anon-id');

// Useful for A/B testing before user signs in
const anonId = localStorage.getItem('experimental_user_id');
if (anonId) {
    posthog.featureFlags.setAnonymousDistinctId(anonId);
    posthog.reloadFeatureFlags();
}

Pause/Resume Flag Reloading

Controls whether feature flags should be automatically reloaded. Useful for performance optimization or testing scenarios.

/**
 * Pause or resume automatic flag reloading
 * @param isPaused - Whether to pause reloading
 */
posthog.featureFlags.setReloadingPaused(isPaused: boolean): void;

Usage Example:

// Pause reloading during batch operations
posthog.featureFlags.setReloadingPaused(true);

// Perform multiple operations
posthog.identify('user-123');
posthog.group('company', 'acme-inc');
posthog.setPersonProperties({ plan: 'enterprise' });

// Resume and reload once
posthog.featureFlags.setReloadingPaused(false);
posthog.reloadFeatureFlags();

// Pause during tests
beforeEach(() => {
    posthog.featureFlags.setReloadingPaused(true);
});

afterEach(() => {
    posthog.featureFlags.setReloadingPaused(false);
});

Check If Flags Are Loaded

Checks if feature flags have finished loading from the server.

/**
 * Checks if feature flags have finished loading
 * @returns True if flags have been loaded, false otherwise
 */
readonly hasLoadedFlags: boolean; // Accessed as posthog.featureFlags.hasLoadedFlags

Usage Examples:

// Check if flags are ready before using them
if (posthog.featureFlags.hasLoadedFlags) {
    const variant = posthog.getFeatureFlag('experiment');
    applyVariant(variant);
} else {
    // Show loading state or use defaults
    applyDefaultVariant();
}

// Wait for flags before critical decision
function renderApp() {
    if (!posthog.featureFlags.hasLoadedFlags) {
        return <LoadingSpinner />;
    }

    const showNewUI = posthog.isFeatureEnabled('new-ui');
    return showNewUI ? <NewApp /> : <OldApp />;
}

Get Feature Flag Details

Gets detailed evaluation information for a single feature flag, including the value, metadata, and evaluation reason.

/**
 * Gets detailed evaluation information for a feature flag
 * @param key - Feature flag key
 * @returns FeatureFlagDetail object with value and metadata, or undefined if not found
 */
posthog.featureFlags.getFeatureFlagDetails(key: string): FeatureFlagDetail | undefined;

Usage Examples:

// Get detailed flag information for debugging
const details = posthog.featureFlags.getFeatureFlagDetails('new-feature');
if (details) {
    console.log('Flag value:', details.value);
    console.log('Evaluation reason:', details.reason);
    console.log('Metadata:', details.metadata);
}

// Debug why a flag has a specific value
function debugFeatureFlag(flagKey: string) {
    const details = posthog.featureFlags.getFeatureFlagDetails(flagKey);

    if (!details) {
        console.log(`Flag "${flagKey}" not found`);
        return;
    }

    console.log(`Flag: ${flagKey}`);
    console.log(`Value: ${details.value}`);
    console.log(`Reason: ${details.reason}`);

    if (details.metadata) {
        console.log('Metadata:', JSON.stringify(details.metadata, null, 2));
    }
}

// Use evaluation reason for analytics
const flagDetails = posthog.featureFlags.getFeatureFlagDetails('experiment');
if (flagDetails) {
    posthog.capture('flag_evaluated', {
        flag_key: 'experiment',
        flag_value: flagDetails.value,
        evaluation_reason: flagDetails.reason
    });
}

Get Remote Config Payload

Fetch encrypted remote config feature flag payloads from PostHog servers. This method is useful for retrieving sensitive configuration data that is encrypted server-side.

/**
 * Fetch encrypted remote config feature flag payload
 * @param key - Feature flag key to retrieve payload for
 * @param callback - Callback function that receives the payload
 */
posthog.featureFlags.getRemoteConfigPayload(key: string, callback: RemoteConfigFeatureFlagCallback): void;

Usage Examples:

import posthog from 'posthog-js';

// Fetch remote config payload
posthog.featureFlags.getRemoteConfigPayload('api-config', (payload) => {
    if (payload) {
        console.log('Remote config loaded:', payload);
        // Use encrypted config data
        initializeAPI(payload);
    }
});

// Example with type-safe payload handling
posthog.featureFlags.getRemoteConfigPayload('home-page-welcome-message', (payload) => {
    if (payload && typeof payload === 'object') {
        const config = payload as { message: string; style: string };
        displayWelcomeMessage(config.message, config.style);
    }
});

Override Feature Flags

Override feature flag values and payloads for local testing and development. This is the modern replacement for the deprecated override() method.

/**
 * Override feature flag values and/or payloads for testing
 * @param overrideOptions - Configuration for flag overrides
 */
posthog.featureFlags.overrideFeatureFlags(overrideOptions: OverrideFeatureFlagsOptions): void;

Usage Examples:

import posthog from 'posthog-js';

// Clear all overrides
posthog.featureFlags.overrideFeatureFlags(false);

// Enable specific flags (as boolean array)
posthog.featureFlags.overrideFeatureFlags(['beta-feature', 'new-ui']);

// Set specific flag variants (as object)
posthog.featureFlags.overrideFeatureFlags({
    'beta-feature': 'variant-a',
    'new-ui': true,
    'pricing-test': 'control'
});

// Override both flags and payloads
posthog.featureFlags.overrideFeatureFlags({
    flags: {
        'beta-feature': 'variant-a',
        'config-flag': true
    },
    payloads: {
        'config-flag': {
            apiEndpoint: 'https://staging.api.example.com',
            maxRetries: 5
        }
    }
});

// Override only payloads (keep flag values from server)
posthog.featureFlags.overrideFeatureFlags({
    payloads: {
        'api-config': { environment: 'development' }
    }
});

// Suppress override warnings
posthog.featureFlags.overrideFeatureFlags({
    flags: ['test-flag'],
    suppressWarning: true
});

Common Patterns

Feature Rollout

Gradually roll out new features:

// Check if new feature is enabled
if (posthog.isFeatureEnabled('new-dashboard')) {
    loadComponent(() => import('./NewDashboard'));
} else {
    loadComponent(() => import('./OldDashboard'));
}

// Rollout based on user properties
posthog.identify(userId, {
    email: user.email,
    plan: user.plan,
    signup_date: user.signupDate
});
posthog.reloadFeatureFlags();

A/B Testing

Run experiments with multivariate flags:

// Get experiment variant
const variant = posthog.getFeatureFlag('checkout-experiment');

switch (variant) {
    case 'control':
        renderControlCheckout();
        break;
    case 'variant-a':
        renderVariantACheckout();
        break;
    case 'variant-b':
        renderVariantBCheckout();
        break;
    default:
        // Flag not loaded or user not in experiment
        renderControlCheckout();
}

// Track experiment exposure
if (variant) {
    posthog.capture('experiment_viewed', {
        experiment: 'checkout-experiment',
        variant: variant
    });
}

Progressive Feature Rollout

Enable features based on user segments:

// Enterprise users get new features first
posthog.onFeatureFlags((flags) => {
    const features = {
        advancedAnalytics: flags['advanced-analytics'],
        customReports: flags['custom-reports'],
        apiAccess: flags['api-access']
    };

    updateUIFeatures(features);
});

// Beta testers
posthog.setPersonProperties({
    beta_tester: true
});
posthog.reloadFeatureFlags();

Kill Switch

Disable features remotely without deployment:

// Check kill switch before critical operations
async function processCriticalOperation() {
    if (posthog.isFeatureEnabled('critical-operation-enabled') === false) {
        console.log('Operation disabled via feature flag');
        return;
    }

    await performOperation();
}

// Conditional feature initialization
posthog.onFeatureFlags((flags) => {
    if (flags['third-party-integration']) {
        initializeThirdPartyIntegration();
    }
});

Feature-Specific Configuration

Use flags with payloads for dynamic configuration:

// Get feature configuration
const searchConfig = posthog.getFeatureFlagPayload('search-config');

if (searchConfig) {
    searchService.configure({
        algorithm: searchConfig.algorithm,
        maxResults: searchConfig.max_results,
        fuzzyMatching: searchConfig.fuzzy_matching,
        boost: searchConfig.boost_factors
    });
}

// Fallback to defaults
const config = posthog.getFeatureFlagPayload('feature-config') || {
    enabled: false,
    settings: defaultSettings
};

Advanced Edge Cases

Handling Undefined Flags

// Always handle undefined cases
function getFeatureWithDefault(flagKey: string, defaultValue: boolean): boolean {
    const value = posthog.isFeatureEnabled(flagKey);
    return value !== undefined ? value : defaultValue;
}

// Usage
const showNewFeature = getFeatureWithDefault('new-feature', false);

// Type-safe multivariate flags
function getVariantWithDefault<T extends string>(
    flagKey: string,
    defaultVariant: T
): T {
    const variant = posthog.getFeatureFlag(flagKey);
    return (variant as T) || defaultVariant;
}

const buttonColor = getVariantWithDefault('button-color', 'blue');

Race Conditions

// ❌ Bad: Check flag before it's loaded
function Component() {
    const enabled = posthog.isFeatureEnabled('new-ui');  // May be undefined
    return enabled ? <NewUI /> : <OldUI />;
}

// ✅ Good: Wait for flags to load
function Component() {
    const [flagsLoaded, setFlagsLoaded] = useState(false);
    const [enabled, setEnabled] = useState(false);
    
    useEffect(() => {
        const unsubscribe = posthog.onFeatureFlags((flags) => {
            setEnabled(flags['new-ui'] || false);
            setFlagsLoaded(true);
        });
        
        return unsubscribe;
    }, []);
    
    if (!flagsLoaded) {
        return <Loading />;
    }
    
    return enabled ? <NewUI /> : <OldUI />;
}

Network Failures

// Handle flag loading failures gracefully
function initializeWithFlagFallbacks() {
    const flagDefaults = {
        'new-dashboard': false,
        'beta-features': false,
        'experimental-mode': false
    };
    
    posthog.onFeatureFlags((flags, variants, context) => {
        if (context?.errorsLoading) {
            console.error('Flag loading error:', context.errorsLoading);
            // Use defaults
            initializeApp(flagDefaults);
        } else {
            // Use loaded flags
            initializeApp({ ...flagDefaults, ...flags });
        }
    });
}

Flag Evaluation Timing

// Check if flags are ready
if (posthog.featureFlags.hasLoadedFlags) {
    const variant = posthog.getFeatureFlag('experiment');
    applyVariant(variant);
} else {
    // Wait for flags
    posthog.onFeatureFlags(() => {
        const variant = posthog.getFeatureFlag('experiment');
        applyVariant(variant);
    });
}

// Or use a promise wrapper
function waitForFlags(): Promise<void> {
    return new Promise((resolve) => {
        if (posthog.featureFlags.hasLoadedFlags) {
            resolve();
        } else {
            posthog.onFeatureFlags(() => resolve())();
        }
    });
}

// Usage
await waitForFlags();
const variant = posthog.getFeatureFlag('experiment');

Caching and Staleness

// Force fresh flag evaluation after important events
async function onUserUpgrade() {
    await upgradeUserPlan(userId);
    
    // Reload flags to get updated targeting
    posthog.reloadFeatureFlags();
    
    // Wait for reload
    await new Promise((resolve) => {
        posthog.onFeatureFlags(() => resolve(undefined))();
    });
    
    // Now flags reflect new plan
    const premiumFeatures = posthog.isFeatureEnabled('premium-features');
}

Performance Considerations

Minimize Flag Checks

// ❌ Bad: Check flag repeatedly in render loop
function render() {
    for (let i = 0; i < 1000; i++) {
        if (posthog.isFeatureEnabled('optimization')) {
            // Checked 1000 times!
        }
    }
}

// ✅ Good: Cache flag value
function render() {
    const optimizationEnabled = posthog.isFeatureEnabled('optimization');
    for (let i = 0; i < 1000; i++) {
        if (optimizationEnabled) {
            // Checked once, used 1000 times
        }
    }
}

Batch Flag Evaluations

// Get all flags at once
const allFlags = {
    newUI: posthog.isFeatureEnabled('new-ui'),
    betaFeatures: posthog.isFeatureEnabled('beta-features'),
    experimentalMode: posthog.isFeatureEnabled('experimental-mode')
};

// Or get all flag details
const flagDetails = posthog.featureFlags.getFlagsWithDetails();

Reduce Reload Frequency

// Pause reloading during batch operations
posthog.featureFlags.setReloadingPaused(true);

// Perform multiple operations
await batchUserUpdate();
await batchGroupUpdate();
await batchPropertyUpdate();

// Resume and reload once
posthog.featureFlags.setReloadingPaused(false);
posthog.reloadFeatureFlags();

Troubleshooting

Flags Not Loading

// Debug flag loading
console.log('Flags endpoint hit:', posthog.flagsEndpointWasHit);
console.log('Has loaded flags:', posthog.featureFlags.hasLoadedFlags);
console.log('All flags:', posthog.featureFlags.getFlags());

// Check configuration
console.log('Flags disabled:', posthog.config.advanced_disable_feature_flags);
console.log('Flags API host:', posthog.config.flags_api_host || posthog.config.api_host);
console.log('Timeout:', posthog.config.feature_flag_request_timeout_ms);

Flag Value Unexpected

// Debug specific flag
const flagKey = 'my-flag';
const detail = posthog.featureFlags.getFeatureFlagDetails(flagKey);

console.log(`Flag: ${flagKey}`);
console.log(`Value: ${detail?.value}`);
console.log(`Reason: ${detail?.reason}`);
console.log(`Metadata:`, detail?.metadata);

// Check targeting
console.log('Distinct ID:', posthog.get_distinct_id());
console.log('Groups:', posthog.getGroups());
console.log('Person properties:', posthog.get_property('email'));

Override Not Working

// Verify override
posthog.featureFlags.overrideFeatureFlags({
    'test-flag': true
});

// Check if applied
const flagValue = posthog.getFeatureFlag('test-flag');
const detail = posthog.featureFlags.getFeatureFlagDetails('test-flag');

console.log('Flag value:', flagValue);
console.log('Evaluation reason:', detail?.reason);  // Should be 'override'

// Clear overrides
posthog.featureFlags.overrideFeatureFlags(false);

Bootstrap Flags Not Applied

// Verify bootstrap config
posthog.init('token', {
    bootstrap: {
        featureFlags: {
            'feature-1': true,
            'feature-2': 'variant-a'
        },
        featureFlagPayloads: {
            'feature-1': { config: 'value' }
        }
    }
});

// Check if bootstrap worked
setTimeout(() => {
    console.log('Flags loaded:', posthog.featureFlags.hasLoadedFlags);
    console.log('Flag values:', posthog.featureFlags.getFlagVariants());
}, 0);

Testing Strategies

Local Testing

// Override flags for testing
if (process.env.NODE_ENV === 'test') {
    posthog.featureFlags.overrideFeatureFlags({
        'new-feature': true,
        'experiment': 'variant-a',
        'beta-mode': false
    }, true);  // suppressWarning: true
}

// Test with different variants
describe('Feature variations', () => {
    it('handles control variant', () => {
        posthog.featureFlags.overrideFeatureFlags({
            'experiment': 'control'
        });
        
        const result = renderComponent();
        expect(result).toMatchControlVariant();
    });
    
    it('handles test variant', () => {
        posthog.featureFlags.overrideFeatureFlags({
            'experiment': 'test'
        });
        
        const result = renderComponent();
        expect(result).toMatchTestVariant();
    });
});

Integration Testing

// Mock flag API responses
async function testWithMockedFlags() {
    // Intercept decide endpoint
    mockHTTP.intercept('POST', '/decide/', {
        featureFlags: {
            'new-feature': true,
            'experiment': 'variant-a'
        },
        featureFlagPayloads: {
            'new-feature': { config: 'test' }
        }
    });
    
    // Initialize and test
    posthog.init('token');
    await waitForFlags();
    
    expect(posthog.isFeatureEnabled('new-feature')).toBe(true);
    expect(posthog.getFeatureFlag('experiment')).toBe('variant-a');
}

URL-Based Testing

// Test different variants via URL
// URL: https://app.com?ph_feature_flag_experiment=variant-a

// PostHog automatically applies URL overrides
const variant = posthog.getFeatureFlag('experiment');
console.log('Testing variant:', variant);  // 'variant-a'

Migration from Other Systems

From LaunchDarkly

// LaunchDarkly: ldClient.variation('flag-key', false)
const value = posthog.isFeatureEnabled('flag-key') ?? false;

// LaunchDarkly: ldClient.variation('flag-key', 'default')
const variant = posthog.getFeatureFlag('flag-key') ?? 'default';

// LaunchDarkly: ldClient.allFlags()
const allFlags = posthog.featureFlags.getFlagVariants();

From Split.io

// Split.io: client.getTreatment('flag-key')
const treatment = posthog.getFeatureFlag('flag-key') || 'control';

// Split.io: client.getTreatmentWithConfig('flag-key')
const treatment = posthog.getFeatureFlag('flag-key');
const config = posthog.getFeatureFlagPayload('flag-key');

From Optimizely

// Optimizely: optimizely.activate('experiment-key', userId)
const variant = posthog.getFeatureFlag('experiment-key');

// Optimizely: optimizely.getVariation('experiment-key', userId)
const variant = posthog.getFeatureFlag('experiment-key');

// Optimizely: optimizely.isFeatureEnabled('feature-key', userId)
const enabled = posthog.isFeatureEnabled('feature-key') ?? false;