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.

privacy.mddocs/reference/

Privacy & Consent

Privacy and consent management allows you to control tracking, respect user preferences, and comply with privacy regulations like GDPR and CCPA.

Capabilities

Opt In to Capturing

Opts the user into tracking and data capture.

/**
 * Opts the user into tracking
 * @param options - Options for opt-in behavior
 */
function opt_in_capturing(options?: {
    captureEventName?: EventName | null | false;
    captureProperties?: Properties;
}): void;

Usage Examples:

// Basic opt-in
posthog.opt_in_capturing();

// Opt-in with custom event
posthog.opt_in_capturing({
    captureEventName: 'user_opted_in',
    captureProperties: {
        opt_in_method: 'cookie_banner',
        timestamp: Date.now()
    }
});

// Opt-in without capturing event
posthog.opt_in_capturing({
    captureEventName: false
});

// Opt-in with consent categories
posthog.opt_in_capturing({
    captureEventName: 'consent_granted',
    captureProperties: {
        consent_analytics: true,
        consent_marketing: true,
        consent_preferences: true
    }
});

Opt Out of Capturing

Opts the user out of tracking and data capture.

/**
 * Opts the user out of tracking
 * Stops all event capture and deletes local data
 */
function opt_out_capturing(): void;

Usage Examples:

// Opt out
posthog.opt_out_capturing();

// Opt out on user request
function onUserOptOut() {
    posthog.opt_out_capturing();
    showConfirmation('You have been opted out of tracking');
}

// Opt out with cleanup
function optOutAndClean() {
    posthog.opt_out_capturing();
    posthog.reset(true); // Also reset device ID
}

Check Opt-In Status

Checks if the user has explicitly opted in to tracking.

/**
 * Checks if the user has opted in to tracking
 * @returns True if user has opted in, false otherwise
 */
function has_opted_in_capturing(): boolean;

Usage Examples:

// Check opt-in status
if (posthog.has_opted_in_capturing()) {
    console.log('User has opted in');
}

// Conditional tracking
function trackUserAction(action) {
    if (posthog.has_opted_in_capturing()) {
        posthog.capture(action);
    }
}

// UI state
function ConsentBanner() {
    const hasOptedIn = posthog.has_opted_in_capturing();
    return hasOptedIn ? null : <ConsentPrompt />;
}

Check Opt-Out Status

Checks if the user has explicitly opted out of tracking.

/**
 * Checks if the user has opted out of tracking
 * @returns True if user has opted out, false otherwise
 */
function has_opted_out_capturing(): boolean;

Usage Examples:

// Check opt-out status
if (posthog.has_opted_out_capturing()) {
    console.log('User has opted out');
}

// Prevent tracking
function captureIfAllowed(event, properties) {
    if (!posthog.has_opted_out_capturing()) {
        posthog.capture(event, properties);
    }
}

// Show privacy status
function PrivacyStatus() {
    const hasOptedOut = posthog.has_opted_out_capturing();
    return (
        <div>
            Tracking: {hasOptedOut ? 'Disabled' : 'Enabled'}
        </div>
    );
}

Get Explicit Consent Status

Gets the explicit consent status (granted, denied, or pending).

/**
 * Gets the explicit consent status
 * @returns 'granted' if opted in, 'denied' if opted out, 'pending' if neither
 */
function get_explicit_consent_status(): 'granted' | 'denied' | 'pending';

Usage Examples:

// Get consent status
const status = posthog.get_explicit_consent_status();
console.log('Consent status:', status);

// Handle all states
switch (posthog.get_explicit_consent_status()) {
    case 'granted':
        initializeAllFeatures();
        break;
    case 'denied':
        showMinimalFeatures();
        break;
    case 'pending':
        showConsentBanner();
        break;
}

// Conditional feature initialization
function initializeFeatures() {
    const consent = posthog.get_explicit_consent_status();

    if (consent === 'granted') {
        posthog.startSessionRecording();
        posthog.startExceptionAutocapture();
    } else if (consent === 'pending') {
        // Wait for user decision
        waitForConsent();
    }
}

Check If Capturing Is Active

Checks if capturing is currently enabled (not opted out).

/**
 * Checks if capturing is currently enabled
 * @returns True if capturing is active, false otherwise
 */
function is_capturing(): boolean;

Usage Examples:

// Check if capturing
if (posthog.is_capturing()) {
    console.log('Capturing is active');
}

// Conditional operations
function performTrackedOperation() {
    const result = doOperation();

    if (posthog.is_capturing()) {
        posthog.capture('operation_completed');
    }

    return result;
}

// Debug logging
if (!posthog.is_capturing()) {
    console.warn('PostHog capturing is disabled');
}

Clear Opt-In/Out Status

Clears the opt-in/out status, resetting to default behavior.

/**
 * Clears the opt-in/out status
 * Resets to default capturing behavior based on config
 */
function clear_opt_in_out_capturing(): void;

Usage Examples:

// Clear status
posthog.clear_opt_in_out_capturing();

// Reset consent state
function resetConsent() {
    posthog.clear_opt_in_out_capturing();
    showConsentBanner();
}

// Allow user to change decision
function changeConsentPreference() {
    posthog.clear_opt_in_out_capturing();
    // User can now choose again
    promptForConsent();
}

Configuration

Opt-Out by Default

Configure PostHog to start with capturing disabled:

// Require opt-in before capturing
posthog.init('token', {
    opt_out_capturing_by_default: true
});

// User must explicitly opt in
// posthog.opt_in_capturing();

// Also disable persistence by default
posthog.init('token', {
    opt_out_capturing_by_default: true,
    opt_out_persistence_by_default: true
});

Respect Do Not Track

Respect the browser's Do Not Track setting:

posthog.init('token', {
    respect_dnt: true
});

Cookieless Mode

Configure cookieless tracking:

// Never use cookies/storage
posthog.init('token', {
    cookieless_mode: 'always'
});

// Use cookies unless user rejects
posthog.init('token', {
    cookieless_mode: 'on_reject'
});

// Always use cookies (default)
posthog.init('token', {
    cookieless_mode: 'never'
});

Property Deny List

Prevent specific properties from being captured:

posthog.init('token', {
    property_denylist: [
        'password',
        'credit_card',
        'ssn',
        'api_key',
        'secret'
    ]
});

Mask Personal Data

Automatically mask personal data properties:

posthog.init('token', {
    mask_personal_data_properties: true,
    custom_personal_data_properties: [
        'email',
        'phone',
        'address',
        'name'
    ]
});

Best Practices

GDPR Compliance

Implement GDPR-compliant consent:

// Initialize with opt-out by default
posthog.init('token', {
    opt_out_capturing_by_default: true,
    opt_out_persistence_by_default: true
});

// Show consent banner
function showConsentBanner() {
    const banner = document.createElement('div');
    banner.innerHTML = `
        <div class="consent-banner">
            <p>We use cookies for analytics. Accept?</p>
            <button id="accept">Accept</button>
            <button id="reject">Reject</button>
        </div>
    `;

    document.getElementById('accept').addEventListener('click', () => {
        posthog.opt_in_capturing({
            captureEventName: 'gdpr_consent_granted'
        });
        banner.remove();
    });

    document.getElementById('reject').addEventListener('click', () => {
        posthog.opt_out_capturing();
        banner.remove();
    });

    document.body.appendChild(banner);
}

// Check consent on page load
if (posthog.get_explicit_consent_status() === 'pending') {
    showConsentBanner();
}

CCPA Compliance

Implement CCPA "Do Not Sell" option:

// Show "Do Not Sell My Personal Information" link
function showCCPALink() {
    const link = document.createElement('a');
    link.textContent = 'Do Not Sell My Personal Information';
    link.href = '#';

    link.addEventListener('click', (e) => {
        e.preventDefault();
        posthog.opt_out_capturing();
        alert('You have opted out of personal information sale');
    });

    document.body.appendChild(link);
}

// Allow users to check and change status
function showPrivacyDashboard() {
    const hasOptedOut = posthog.has_opted_out_capturing();

    if (hasOptedOut) {
        showOptInButton();
    } else {
        showOptOutButton();
    }
}

Granular Consent

Implement consent for specific tracking features:

class ConsentManager {
    constructor() {
        this.consent = {
            analytics: false,
            advertising: false,
            personalization: false,
            sessionRecording: false,
            errorTracking: false
        };
    }

    updateConsent(category, granted) {
        this.consent[category] = granted;
        this.applyConsent();
    }

    applyConsent() {
        // Basic analytics
        if (this.consent.analytics) {
            posthog.opt_in_capturing();
        } else {
            posthog.opt_out_capturing();
        }

        // Session recording
        if (this.consent.sessionRecording) {
            posthog.startSessionRecording();
        } else {
            posthog.stopSessionRecording();
        }

        // Error tracking
        if (this.consent.errorTracking) {
            posthog.startExceptionAutocapture();
        } else {
            posthog.stopExceptionAutocapture();
        }
    }

    getConsentState() {
        return { ...this.consent };
    }
}

// Usage
const consentManager = new ConsentManager();

consentManager.updateConsent('analytics', true);
consentManager.updateConsent('sessionRecording', true);
consentManager.updateConsent('errorTracking', false);

Consent UI Components

Build reusable consent components:

// React consent banner
function ConsentBanner() {
    const [visible, setVisible] = useState(true);

    useEffect(() => {
        const status = posthog.get_explicit_consent_status();
        if (status !== 'pending') {
            setVisible(false);
        }
    }, []);

    const handleAccept = () => {
        posthog.opt_in_capturing({
            captureEventName: 'consent_accepted'
        });
        setVisible(false);
    };

    const handleReject = () => {
        posthog.opt_out_capturing();
        setVisible(false);
    };

    if (!visible) return null;

    return (
        <div className="consent-banner">
            <p>We use cookies to improve your experience.</p>
            <button onClick={handleAccept}>Accept</button>
            <button onClick={handleReject}>Reject</button>
        </div>
    );
}

// Privacy settings page
function PrivacySettings() {
    const [capturing, setCapturing] = useState(posthog.is_capturing());

    const handleToggle = () => {
        if (capturing) {
            posthog.opt_out_capturing();
            setCapturing(false);
        } else {
            posthog.opt_in_capturing();
            setCapturing(true);
        }
    };

    return (
        <div>
            <h2>Privacy Settings</h2>
            <label>
                <input
                    type="checkbox"
                    checked={capturing}
                    onChange={handleToggle}
                />
                Enable Analytics
            </label>
        </div>
    );
}

Common Patterns

Cookie Consent Integration

Integrate with cookie consent libraries:

// OneTrust integration
window.OptanonWrapper = function() {
    const activeGroups = window.OnetrustActiveGroups;

    // Check if analytics cookies are accepted
    if (activeGroups.includes('C0002')) {
        posthog.opt_in_capturing({
            captureEventName: 'onetrust_consent_granted'
        });
    } else {
        posthog.opt_out_capturing();
    }
};

// CookieBot integration
window.addEventListener('CookiebotOnAccept', function() {
    if (Cookiebot.consent.statistics) {
        posthog.opt_in_capturing({
            captureEventName: 'cookiebot_consent_granted'
        });
    }
});

window.addEventListener('CookiebotOnDecline', function() {
    posthog.opt_out_capturing();
});

// Google Consent Mode integration
function updatePostHogConsent(consentSettings) {
    if (consentSettings.analytics_storage === 'granted') {
        posthog.opt_in_capturing();
    } else {
        posthog.opt_out_capturing();
    }
}

gtag('consent', 'default', {
    analytics_storage: 'denied'
});

gtag('consent', 'update', {
    analytics_storage: 'granted'
});

updatePostHogConsent({ analytics_storage: 'granted' });

Conditional Feature Loading

Load features based on consent:

async function initializeWithConsent() {
    const consent = posthog.get_explicit_consent_status();

    if (consent === 'pending') {
        // Wait for user decision
        await promptForConsent();
    }

    if (posthog.has_opted_in_capturing()) {
        // Load full features
        posthog.startSessionRecording();
        posthog.startExceptionAutocapture();

        // Load analytics-dependent features
        await loadUserRecommendations();
        await loadPersonalizedContent();
    } else {
        // Load minimal features
        showGenericContent();
    }
}

Consent Expiration

Implement consent expiration:

const CONSENT_EXPIRY_DAYS = 365;

function setConsentWithExpiry(granted) {
    const expiry = Date.now() + (CONSENT_EXPIRY_DAYS * 24 * 60 * 60 * 1000);

    localStorage.setItem('consent_expiry', expiry.toString());

    if (granted) {
        posthog.opt_in_capturing();
    } else {
        posthog.opt_out_capturing();
    }
}

function checkConsentExpiry() {
    const expiry = localStorage.getItem('consent_expiry');

    if (expiry && Date.now() > parseInt(expiry)) {
        // Consent expired, reset
        posthog.clear_opt_in_out_capturing();
        localStorage.removeItem('consent_expiry');
        showConsentBanner();
    }
}

// Check on page load
checkConsentExpiry();

Geolocation-Based Consent

Show consent based on user location:

async function initializeWithGeolocation() {
    const country = await getUserCountry();

    const gdprCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB'];

    if (gdprCountries.includes(country)) {
        // Require explicit consent for GDPR countries
        posthog.init('token', {
            opt_out_capturing_by_default: true
        });

        if (posthog.get_explicit_consent_status() === 'pending') {
            showGDPRConsentBanner();
        }
    } else {
        // Auto opt-in for non-GDPR countries (with opt-out option)
        posthog.init('token', {
            opt_out_capturing_by_default: false
        });

        showOptOutLink();
    }
}

Consent Change Tracking

Track consent changes for audit:

function trackConsentChange(oldStatus, newStatus) {
    // Log to backend for compliance
    fetch('/api/consent-log', {
        method: 'POST',
        body: JSON.stringify({
            user_id: posthog.get_distinct_id(),
            old_status: oldStatus,
            new_status: newStatus,
            timestamp: Date.now(),
            user_agent: navigator.userAgent
        })
    });
}

function optInWithTracking() {
    const oldStatus = posthog.get_explicit_consent_status();

    posthog.opt_in_capturing({
        captureEventName: 'consent_granted'
    });

    const newStatus = posthog.get_explicit_consent_status();
    trackConsentChange(oldStatus, newStatus);
}

function optOutWithTracking() {
    const oldStatus = posthog.get_explicit_consent_status();

    posthog.opt_out_capturing();

    const newStatus = posthog.get_explicit_consent_status();
    trackConsentChange(oldStatus, newStatus);
}

Privacy-First Initialization

Initialize with maximum privacy by default:

posthog.init('token', {
    // Require opt-in
    opt_out_capturing_by_default: true,
    opt_out_persistence_by_default: true,

    // Respect browser settings
    respect_dnt: true,

    // Cookieless mode
    cookieless_mode: 'always',

    // Disable features by default
    disable_session_recording: true,
    capture_exceptions: false,
    autocapture: false,

    // Mask all data
    mask_all_text: true,
    mask_all_element_attributes: true,
    property_denylist: [
        'email', 'name', 'phone', 'address',
        'credit_card', 'ssn', 'password'
    ]
});

// Enable features after consent
function onUserConsent() {
    posthog.opt_in_capturing();
    posthog.set_config({
        autocapture: true,
        capture_exceptions: true
    });
    posthog.startSessionRecording();
}

Consent Persistence

Persist consent across sessions:

class ConsentStorage {
    static save(status) {
        localStorage.setItem('posthog_consent', status);
        localStorage.setItem('posthog_consent_date', Date.now().toString());
    }

    static load() {
        return localStorage.getItem('posthog_consent');
    }

    static getDate() {
        return localStorage.getItem('posthog_consent_date');
    }

    static clear() {
        localStorage.removeItem('posthog_consent');
        localStorage.removeItem('posthog_consent_date');
    }
}

// Initialize with saved consent
function initializeWithSavedConsent() {
    const savedConsent = ConsentStorage.load();

    if (savedConsent === 'granted') {
        posthog.opt_in_capturing();
    } else if (savedConsent === 'denied') {
        posthog.opt_out_capturing();
    } else {
        // No saved consent, show banner
        showConsentBanner();
    }
}

// Save when consent changes
function handleConsentChange(granted) {
    if (granted) {
        posthog.opt_in_capturing();
        ConsentStorage.save('granted');
    } else {
        posthog.opt_out_capturing();
        ConsentStorage.save('denied');
    }
}

Advanced Compliance Patterns

Multi-Region Compliance

// Handle different privacy laws by region
interface RegionConfig {
    requiresExplicitConsent: boolean;
    dataResidency: 'us' | 'eu';
    retentionDays: number;
}

const regionConfigs: Record<string, RegionConfig> = {
    'US': { requiresExplicitConsent: false, dataResidency: 'us', retentionDays: 730 },
    'GB': { requiresExplicitConsent: true, dataResidency: 'eu', retentionDays: 365 },
    'DE': { requiresExplicitConsent: true, dataResidency: 'eu', retentionDays: 365 },
    'FR': { requiresExplicitConsent: true, dataResidency: 'eu', retentionDays: 365 }
};

async function initializeWithRegionalCompliance() {
    const userRegion = await detectUserRegion();
    const config = regionConfigs[userRegion] || regionConfigs['US'];
    
    const apiHost = config.dataResidency === 'eu'
        ? 'https://eu.i.posthog.com'
        : 'https://us.i.posthog.com';
    
    posthog.init('token', {
        api_host: apiHost,
        opt_out_capturing_by_default: config.requiresExplicitConsent
    });
    
    if (config.requiresExplicitConsent) {
        const consent = getStoredConsent();
        if (!consent) {
            showConsentBanner();
        } else if (consent === 'granted') {
            posthog.opt_in_capturing();
        }
    }
}

Consent Management Platform Integration

// OneTrust CMP integration
declare global {
    interface Window {
        OneTrust?: {
            OnConsentChanged: (callback: () => void) => void;
        };
        OnetrustActiveGroups?: string;
    }
}

function initializeOneTrustIntegration() {
    // Check initial consent
    const checkConsent = () => {
        const activeGroups = window.OnetrustActiveGroups || '';
        const analyticsConsent = activeGroups.includes('C0002');
        
        if (analyticsConsent) {
            posthog.opt_in_capturing({
                captureEventName: 'onetrust_consent_granted',
                captureProperties: {
                    consent_groups: activeGroups
                }
            });
        } else {
            posthog.opt_out_capturing();
        }
    };
    
    // Check on load
    checkConsent();
    
    // Listen for changes
    window.OneTrust?.OnConsentChanged(() => {
        checkConsent();
    });
}

California Consumer Privacy Act (CCPA) Compliance

// Implement CCPA "Do Not Sell" functionality
class CCPACompliance {
    private readonly DO_NOT_SELL_KEY = 'ccpa_do_not_sell';
    
    initialize() {
        // Check if user has opted out of sale
        const doNotSell = localStorage.getItem(this.DO_NOT_SELL_KEY) === 'true';
        
        if (doNotSell) {
            this.optOutOfSale();
        }
        
        // Add "Do Not Sell" link to footer
        this.addDoNotSellLink();
    }
    
    optOutOfSale() {
        // Opt out of tracking
        posthog.opt_out_capturing();
        
        // Store preference
        localStorage.setItem(this.DO_NOT_SELL_KEY, 'true');
        
        // Capture opt-out event before disabling
        posthog.capture('ccpa_do_not_sell', {
            opted_out_at: new Date().toISOString()
        });
    }
    
    optInToSale() {
        // Opt back in
        posthog.opt_in_capturing({
            captureEventName: 'ccpa_sale_opted_in'
        });
        
        // Clear preference
        localStorage.removeItem(this.DO_NOT_SELL_KEY);
    }
    
    addDoNotSellLink() {
        const link = document.createElement('a');
        link.href = '#';
        link.textContent = 'Do Not Sell My Personal Information';
        link.className = 'ccpa-opt-out-link';
        
        link.addEventListener('click', (e) => {
            e.preventDefault();
            this.showOptOutModal();
        });
        
        document.querySelector('.footer')?.appendChild(link);
    }
    
    showOptOutModal() {
        const confirmed = confirm(
            'You are about to opt out of the sale of your personal information. Continue?'
        );
        
        if (confirmed) {
            this.optOutOfSale();
            alert('You have successfully opted out.');
        }
    }
}

Children's Online Privacy Protection Act (COPPA) Compliance

// Disable tracking for users under 13
function initializeCOPPACompliance(userAge?: number) {
    if (userAge && userAge < 13) {
        posthog.init('token', {
            // Completely disable tracking for children
            opt_out_capturing_by_default: true,
            opt_out_persistence_by_default: true,
            disable_persistence: true,
            disable_session_recording: true,
            disable_surveys: true,
            autocapture: false,
            capture_pageview: false
        });
        
        // Ensure opt-out is persistent
        posthog.opt_out_capturing();
        
        console.log('COPPA compliance mode: Tracking disabled for user under 13');
    } else {
        // Normal initialization for adults
        posthog.init('token', {
            opt_out_capturing_by_default: true
        });
        
        // Show age-appropriate consent
        showAdultConsentBanner();
    }
}

Data Subject Rights Implementation

Right to Access

// Provide user access to their data
async function getUserData() {
    const distinctId = posthog.get_distinct_id();
    const groups = posthog.getGroups();
    
    return {
        distinct_id: distinctId,
        groups: groups,
        // Note: Full data export must be done via PostHog API
        posthog_profile_url: `https://app.posthog.com/person/${distinctId}`
    };
}

Right to Deletion

// Handle deletion request
async function handleDeletionRequest(userId: string) {
    // Reset local state
    posthog.reset(true);  // Reset including device ID
    
    // Opt out to prevent future tracking
    posthog.opt_out_capturing();
    
    // Clear all local storage
    Object.keys(localStorage).forEach(key => {
        if (key.startsWith('ph_')) {
            localStorage.removeItem(key);
        }
    });
    
    // Log deletion request for backend processing
    await logDeletionRequest(userId);
    
    console.log('User data deletion initiated');
}

Right to Rectification

// Update incorrect user properties
async function rectifyUserData(corrections: Properties) {
    // Update properties in PostHog
    posthog.setPersonProperties(corrections);
    
    // Log rectification for audit trail
    posthog.capture('data_rectified', {
        rectified_fields: Object.keys(corrections),
        rectified_at: new Date().toISOString()
    });
}

Right to Portability

// Export user data in portable format
async function exportUserData() {
    const userData = {
        distinct_id: posthog.get_distinct_id(),
        groups: posthog.getGroups(),
        session_id: posthog.get_session_id(),
        feature_flags: posthog.featureFlags.getFlagVariants(),
        // Note: Full event history requires PostHog API access
        export_date: new Date().toISOString()
    };
    
    // Convert to JSON for portability
    const dataBlob = new Blob(
        [JSON.stringify(userData, null, 2)],
        { type: 'application/json' }
    );
    
    // Download file
    const url = URL.createObjectURL(dataBlob);
    const link = document.createElement('a');
    link.href = url;
    link.download = `posthog-data-${userData.distinct_id}.json`;
    link.click();
    
    URL.revokeObjectURL(url);
}

Consent Lifecycle Management

Complete Consent Flow

class ConsentLifecycleManager {
    private readonly CONSENT_VERSION = '2.0';
    
    initialize() {
        const stored = this.getStoredConsent();
        
        if (!stored || stored.version !== this.CONSENT_VERSION) {
            // No consent or version changed, show banner
            this.showConsentBanner();
        } else {
            // Apply stored consent
            this.applyConsent(stored.granted);
        }
    }
    
    showConsentBanner() {
        // Show UI with granular options
        const banner = this.createGranularConsentUI({
            onSave: (choices) => {
                this.saveConsent(choices);
                this.applyConsent(choices.analytics);
            }
        });
    }
    
    saveConsent(choices: {
        analytics: boolean;
        marketing: boolean;
        functional: boolean;
    }) {
        const consent = {
            version: this.CONSENT_VERSION,
            granted: choices.analytics,
            choices: choices,
            timestamp: new Date().toISOString()
        };
        
        localStorage.setItem('posthog_consent', JSON.stringify(consent));
    }
    
    getStoredConsent(): {
        version: string;
        granted: boolean;
        choices: any;
        timestamp: string;
    } | null {
        const stored = localStorage.getItem('posthog_consent');
        return stored ? JSON.parse(stored) : null;
    }
    
    applyConsent(granted: boolean) {
        if (granted) {
            posthog.opt_in_capturing({
                captureEventName: 'consent_applied',
                captureProperties: {
                    consent_version: this.CONSENT_VERSION
                }
            });
        } else {
            posthog.opt_out_capturing();
        }
    }
    
    revokeConsent() {
        // Revoke consent and delete data
        posthog.opt_out_capturing();
        posthog.reset(true);
        localStorage.removeItem('posthog_consent');
        
        console.log('Consent revoked and data cleared');
    }
    
    createGranularConsentUI(options: any) {
        // Implementation of granular consent UI
        // Returns banner element
    }
}

Testing Privacy Compliance

Automated Compliance Tests

describe('Privacy Compliance', () => {
    it('requires opt-in before tracking', () => {
        posthog.init('token', {
            opt_out_capturing_by_default: true
        });
        
        expect(posthog.is_capturing()).toBe(false);
        
        posthog.opt_in_capturing();
        
        expect(posthog.is_capturing()).toBe(true);
    });
    
    it('respects Do Not Track', () => {
        // Mock DNT
        Object.defineProperty(navigator, 'doNotTrack', {
            value: '1',
            configurable: true
        });
        
        posthog.init('token', {
            respect_dnt: true
        });
        
        expect(posthog.is_capturing()).toBe(false);
    });
    
    it('deletes data on opt-out', () => {
        posthog.init('token');
        posthog.identify('user-123');
        
        const distinctIdBefore = posthog.get_distinct_id();
        
        posthog.opt_out_capturing();
        
        // Should clear local data
        expect(posthog.is_capturing()).toBe(false);
    });
});

Troubleshooting Privacy Issues

Consent Not Persisting

// Debug consent persistence
function debugConsent() {
    console.log('Consent status:', posthog.get_explicit_consent_status());
    console.log('Has opted in:', posthog.has_opted_in_capturing());
    console.log('Has opted out:', posthog.has_opted_out_capturing());
    console.log('Is capturing:', posthog.is_capturing());
    
    // Check storage
    console.log('Consent storage:', {
        localStorage: localStorage.getItem('__ph_consent'),
        cookie: document.cookie.includes('ph_consent')
    });
    
    // Check config
    console.log('Config:', {
        opt_out_by_default: posthog.config.opt_out_capturing_by_default,
        consent_name: posthog.config.consent_persistence_name
    });
}

Opt-Out Not Working

// Verify opt-out
function verifyOptOut() {
    posthog.opt_out_capturing();
    
    // Wait for state to update
    setTimeout(() => {
        console.log('Opt-out verification:', {
            is_capturing: posthog.is_capturing(),
            has_opted_out: posthog.has_opted_out_capturing(),
            status: posthog.get_explicit_consent_status()
        });
        
        // Try capturing event (should fail)
        const result = posthog.capture('test_event');
        console.log('Capture after opt-out:', result);  // Should be undefined or void
    }, 100);
}

DNT Not Respected

// Force DNT respect
function enforceDNT() {
    if (navigator.doNotTrack === '1' || 
        (navigator as any).doNotTrack === 'yes' ||
        (window as any).doNotTrack === '1') {
        
        console.log('DNT detected, disabling tracking');
        
        posthog.opt_out_capturing();
        
        // Ensure it can't be overridden
        const originalOptIn = posthog.opt_in_capturing;
        posthog.opt_in_capturing = () => {
            console.warn('Opt-in blocked due to DNT');
        };
    }
}

Cookie Consent Library Integrations

Cookiebot Integration

// Full Cookiebot integration
declare global {
    interface Window {
        Cookiebot?: {
            consent: {
                necessary: boolean;
                preferences: boolean;
                statistics: boolean;
                marketing: boolean;
            };
            consented: boolean;
        };
    }
}

function initializeCookiebotIntegration() {
    posthog.init('token', {
        opt_out_capturing_by_default: true
    });
    
    // Handle consent acceptance
    window.addEventListener('CookiebotOnAccept', () => {
        if (window.Cookiebot?.consent.statistics) {
            posthog.opt_in_capturing({
                captureEventName: 'cookiebot_consent_granted',
                captureProperties: {
                    consent_preferences: window.Cookiebot.consent.preferences,
                    consent_marketing: window.Cookiebot.consent.marketing
                }
            });
            
            // Enable features based on consent
            if (window.Cookiebot.consent.statistics) {
                posthog.startSessionRecording();
            }
        }
    });
    
    // Handle consent decline
    window.addEventListener('CookiebotOnDecline', () => {
        posthog.opt_out_capturing();
        posthog.stopSessionRecording();
    });
    
    // Check if already consented
    if (window.Cookiebot?.consented && window.Cookiebot?.consent.statistics) {
        posthog.opt_in_capturing();
    }
}

Usercentrics Integration

// Usercentrics CMP integration
declare global {
    interface Window {
        UC_UI?: {
            isInitialized: () => boolean;
            getServicesBaseInfo: () => Array<{
                id: string;
                name: string;
                consent: { status: boolean };
            }>;
        };
    }
}

function initializeUsercentricsIntegration() {
    const POSTHOG_SERVICE_ID = 'PostHog';
    
    posthog.init('token', {
        opt_out_capturing_by_default: true
    });
    
    // Wait for Usercentrics
    const checkConsent = () => {
        if (window.UC_UI?.isInitialized()) {
            const services = window.UC_UI.getServicesBaseInfo();
            const posthogService = services.find(s => s.name === POSTHOG_SERVICE_ID);
            
            if (posthogService?.consent.status) {
                posthog.opt_in_capturing({
                    captureEventName: 'usercentrics_consent_granted'
                });
            } else {
                posthog.opt_out_capturing();
            }
        } else {
            // Retry
            setTimeout(checkConsent, 100);
        }
    };
    
    checkConsent();
    
    // Listen for consent changes
    window.addEventListener('ucEvent', (event: any) => {
        if (event.detail?.event === 'consent_status') {
            checkConsent();
        }
    });
}

Privacy Policy Requirements

Essential Disclosures

Your privacy policy should disclose:

  1. Data Collection: What data PostHog collects (events, properties, device info)
  2. Purpose: Why you're collecting it (analytics, product improvement)
  3. Storage: Where data is stored (US/EU regions)
  4. Retention: How long data is kept
  5. Third Parties: That PostHog (third-party service) processes the data
  6. Rights: User rights (access, deletion, opt-out)
  7. Cookies: That cookies/localStorage are used for tracking

Privacy Policy Template Section

Analytics and Tracking

We use PostHog to understand how users interact with our application. PostHog collects:
- Page views and navigation patterns
- Button clicks and user interactions
- Device information (browser, OS, screen size)
- Session recordings (with sensitive data masked)

This data is processed by PostHog Inc. and stored in [US/EU] data centers. You can opt out of tracking at any time through our privacy settings.

For more information, see PostHog's privacy policy: https://posthog.com/privacy

See Also

  • Initialization - Privacy-focused configuration
  • Session Recording - Recording privacy settings
  • Events - Event filtering and property denylists
  • Advanced Features - Group analytics and consent management