# Session Recording

Session recording captures user interactions, page views, and behaviors to help you understand how users experience your application.

## Capabilities

### Start Session Recording

Starts session recording, optionally overriding trigger requirements.

```typescript { .api }
/**
 * Starts session recording
 * @param override - Override specific trigger requirements, or true to override all
 */
function startSessionRecording(override?: {
    sampling?: boolean;
    linked_flag?: boolean;
    url_trigger?: boolean;
    event_trigger?: boolean;
} | true): void;
```

**Usage Examples:**

```typescript
// Start recording unconditionally
posthog.startSessionRecording();

// Start with all overrides
posthog.startSessionRecording(true);

// Override specific triggers
posthog.startSessionRecording({
    sampling: true,        // Override sample rate
    linked_flag: true,     // Override feature flag requirement
    url_trigger: false,    // Still require URL match
    event_trigger: false   // Still require event trigger
});

// Start after user action
function onUserConsent() {
    posthog.startSessionRecording();
}

// Conditional recording
if (user.plan === 'premium') {
    posthog.startSessionRecording();
}
```

### Stop Session Recording

Stops the current session recording.

```typescript { .api }
/**
 * Stops the current session recording
 */
function stopSessionRecording(): void;
```

**Usage Examples:**

```typescript
// Stop recording
posthog.stopSessionRecording();

// Stop on sensitive page
router.on('route', (route) => {
    if (route.path.includes('/payment')) {
        posthog.stopSessionRecording();
    }
});

// Stop on user request
function disableRecording() {
    posthog.stopSessionRecording();
    localStorage.setItem('recording_disabled', 'true');
}
```

### Check Recording Status

Checks if session recording is currently active.

```typescript { .api }
/**
 * Checks if session recording is currently active
 * @returns True if recording is active, false otherwise
 */
function sessionRecordingStarted(): boolean;
```

**Usage Examples:**

```typescript
// Check status
if (posthog.sessionRecordingStarted()) {
    console.log('Session is being recorded');
}

// Conditional UI
function RecordingIndicator() {
    const isRecording = posthog.sessionRecordingStarted();
    return isRecording ? <div>Recording</div> : null;
}

// Start if not already started
if (!posthog.sessionRecordingStarted()) {
    posthog.startSessionRecording();
}
```

### Get Session ID

Gets the current session ID.

```typescript { .api }
/**
 * Gets the current session ID
 * @returns The current session ID
 */
function get_session_id(): string;
```

**Usage Examples:**

```typescript
// Get session ID
const sessionId = posthog.get_session_id();
console.log('Current session:', sessionId);

// Include in API calls
fetch('/api/data', {
    headers: {
        'X-Session-ID': posthog.get_session_id()
    }
});

// Track session across services
const sessionData = {
    sessionId: posthog.get_session_id(),
    userId: posthog.get_distinct_id()
};
```

### Get Session Replay URL

Gets the URL to view the session replay in PostHog.

```typescript { .api }
/**
 * Gets the URL to the session replay
 * @param options - Options for replay URL generation
 * @returns URL to view the session replay
 */
function get_session_replay_url(options?: {
    withTimestamp?: boolean;
    timestampLookBack?: number;
}): string;
```

**Usage Examples:**

```typescript
// Get basic replay URL
const replayUrl = posthog.get_session_replay_url();
console.log('View replay:', replayUrl);

// Get URL with current timestamp
const urlWithTime = posthog.get_session_replay_url({
    withTimestamp: true
});

// Get URL with lookback (seconds before current time)
const urlWithLookback = posthog.get_session_replay_url({
    withTimestamp: true,
    timestampLookBack: 30 // 30 seconds before now
});

// Share replay with support
function shareReplayWithSupport(issueDescription) {
    const replayUrl = posthog.get_session_replay_url({
        withTimestamp: true
    });

    sendToSupport({
        description: issueDescription,
        replayUrl: replayUrl,
        sessionId: posthog.get_session_id()
    });
}
```

### Session ID Change Callback

Registers a callback that fires when the session ID changes.

```typescript { .api }
/**
 * Registers a callback for when the session ID changes
 * @param callback - Function to call when session changes
 * @returns Function to unsubscribe from callback
 */
function onSessionId(callback: SessionIdChangedCallback): () => void;
```

**Usage Examples:**

```typescript
// Listen for session changes
const unsubscribe = posthog.onSessionId((sessionId, windowId) => {
    console.log('New session:', sessionId);
    console.log('Window ID:', windowId);

    // Track session change
    trackSessionChange(sessionId);
});

// Stop listening
unsubscribe();

// React to session changes
posthog.onSessionId((sessionId) => {
    // Update UI with new session
    updateSessionDisplay(sessionId);

    // Sync with backend
    syncSessionToBackend(sessionId);
});
```

## SessionRecording Sub-API

The `posthog.sessionRecording` property provides access to advanced session recording controls. These methods allow programmatic control over recording behavior and are primarily used for debugging or advanced use cases.

### Check Recording Status

Checks if session recording is currently active.

```typescript { .api }
/**
 * Checks if session recording is currently active
 * @returns True if recording is active, false otherwise
 */
readonly started: boolean; // Accessed as posthog.sessionRecording.started
```

**Usage Examples:**

```typescript
// Check if recording is active
if (posthog.sessionRecording && posthog.sessionRecording.started) {
    console.log('Session recording is active');
} else {
    console.log('Session recording is not active');
}

// Show recording indicator
function RecordingStatusIndicator() {
    const isRecording = posthog.sessionRecording?.started ?? false;
    return isRecording ? <span>🔴 Recording</span> : null;
}

// Conditional logic based on recording state
function handleSensitiveAction() {
    if (posthog.sessionRecording?.started) {
        // Pause recording for sensitive operations
        posthog.stopSessionRecording();
        performSensitiveAction();
        posthog.startSessionRecording();
    } else {
        performSensitiveAction();
    }
}
```

### Override Linked Flag

Programmatically overrides the linked feature flag requirement for session recording.

```typescript { .api }
/**
 * Overrides the linked feature flag requirement
 * Allows recording to start regardless of feature flag state
 */
posthog.sessionRecording.overrideLinkedFlag(): void;
```

**Usage Examples:**

```typescript
// Force recording regardless of feature flag
if (posthog.sessionRecording) {
    posthog.sessionRecording.overrideLinkedFlag();
    posthog.startSessionRecording();
}

// Record VIP user sessions regardless of flag
if (user.isVIP) {
    posthog.sessionRecording?.overrideLinkedFlag();
    posthog.startSessionRecording();
}
```

### Override Sampling

Programmatically overrides the sampling decision for session recording.

```typescript { .api }
/**
 * Overrides the sampling decision
 * Allows recording to start regardless of configured sample rate
 */
posthog.sessionRecording.overrideSampling(): void;
```

**Usage Examples:**

```typescript
// Force recording regardless of sample rate
if (posthog.sessionRecording) {
    posthog.sessionRecording.overrideSampling();
    posthog.startSessionRecording();
}

// Record sessions for support tickets
function openSupportTicket() {
    posthog.sessionRecording?.overrideSampling();
    posthog.startSessionRecording();

    const replayUrl = posthog.get_session_replay_url();
    createTicket({ replayUrl });
}
```

### Override Trigger

Programmatically overrides specific trigger requirements for session recording.

```typescript { .api }
/**
 * Overrides specific trigger requirement
 * @param triggerType - Type of trigger to override ('url' or 'event')
 */
posthog.sessionRecording.overrideTrigger(triggerType: 'url' | 'event'): void;
```

**Usage Examples:**

```typescript
// Override URL trigger requirement
if (posthog.sessionRecording) {
    posthog.sessionRecording.overrideTrigger('url');
    posthog.startSessionRecording();
}

// Override event trigger requirement
if (posthog.sessionRecording) {
    posthog.sessionRecording.overrideTrigger('event');
    posthog.startSessionRecording();
}

// Start recording on button click, ignoring triggers
function handleDebugRecording() {
    if (posthog.sessionRecording) {
        posthog.sessionRecording.overrideTrigger('url');
        posthog.sessionRecording.overrideTrigger('event');
        posthog.startSessionRecording();
    }
}
```

### Force Localhost Network Capture (Debug Only)

Debug property to enable network capture on localhost. This is intended for development and debugging purposes only.

```typescript { .api }
/**
 * Forces network capture on localhost for debugging
 * WARNING: Development/debugging use only
 */
posthog.sessionRecording._forceAllowLocalhostNetworkCapture: boolean;
```

**Usage Examples:**

```typescript
// Enable network capture in local development
if (process.env.NODE_ENV === 'development' && posthog.sessionRecording) {
    posthog.sessionRecording._forceAllowLocalhostNetworkCapture = true;
    posthog.startSessionRecording();
}

// Debug network requests locally
if (window.location.hostname === 'localhost' && posthog.sessionRecording) {
    posthog.sessionRecording._forceAllowLocalhostNetworkCapture = true;
}
```

**Note:** This property is prefixed with `_` to indicate it's for debugging purposes and should not be used in production code.

## Types

### SessionIdChangedCallback

```typescript { .api }
/**
 * Callback invoked when session ID changes
 */
type SessionIdChangedCallback = (sessionId: string, windowId: string) => void;
```

### SessionRecordingOptions

```typescript { .api }
/**
 * Configuration for session recording
 */
interface SessionRecordingOptions {
    /**
     * Enable or disable session recording
     * @default undefined (uses server-side config)
     */
    enabled?: boolean;

    /**
     * Mask all input fields
     * @default false
     */
    maskAllInputs?: boolean;

    /**
     * CSS selector for text elements to mask
     */
    maskTextSelector?: string;

    /**
     * CSS selector for elements to completely block from recording
     */
    blockSelector?: string;

    /**
     * Record cross-origin iframes
     * @default false
     */
    recordCrossOriginIframes?: boolean;

    /**
     * Sample rate for session recording (0-1)
     * 0.1 = 10% of sessions recorded
     */
    sampleRate?: number;

    /**
     * Minimum session duration to record (milliseconds)
     */
    minimumDurationMilliseconds?: number;

    /**
     * Feature flag controlling recording
     */
    linkedFlag?: string | FlagVariant;

    /**
     * Enable canvas recording
     * @default false
     */
    recordCanvas?: boolean;

    /**
     * Capture console logs in recordings
     * @default true
     */
    consoleLogRecordingEnabled?: boolean;

    /**
     * Capture network activity
     */
    networkPayloadCapture?: {
        /**
         * Record request/response headers
         */
        recordHeaders?: boolean | {
            request: boolean;
            response: boolean;
        };

        /**
         * Record request/response bodies
         */
        recordBody?: boolean | string[] | {
            request: boolean | string[];
            response: boolean | string[];
        };
    };

    /**
     * Capture performance data
     * @default true
     */
    capturePerformance?: boolean;

    /**
     * URL triggers for recording
     */
    urlTriggers?: string[];

    /**
     * Event triggers for recording
     */
    eventTriggers?: string[];

    /**
     * Mask text content
     * @default false
     */
    maskTextContent?: boolean;

    /**
     * Mask captured network requests
     */
    maskCapturedNetworkRequestFn?: (data: NetworkRequest) => NetworkRequest | null;
}
```

### FlagVariant

```typescript { .api }
/**
 * Feature flag variant configuration
 */
interface FlagVariant {
    /**
     * Feature flag key
     */
    flag: string;

    /**
     * Required variant value
     */
    variant: string;
}
```

### NetworkRequest

```typescript { .api }
/**
 * Network request data captured during recording
 */
interface NetworkRequest {
    /**
     * Request URL
     */
    url: string;

    /**
     * HTTP method
     */
    method: string;

    /**
     * Request headers
     */
    headers?: Record<string, string>;

    /**
     * Request body
     */
    body?: string;

    /**
     * Response status
     */
    status?: number;

    /**
     * Response headers
     */
    responseHeaders?: Record<string, string>;

    /**
     * Response body
     */
    responseBody?: string;
}
```

## Configuration

### Session Recording Settings

Configure session recording during initialization:

```typescript { .api }
// Basic configuration
posthog.init('token', {
    session_recording: {
        enabled: true,
        maskAllInputs: true,
        consoleLogRecordingEnabled: true
    }
});

// Privacy-focused configuration
posthog.init('token', {
    session_recording: {
        enabled: true,
        maskAllInputs: true,
        maskTextContent: true,
        blockSelector: '.sensitive-data',
        networkPayloadCapture: {
            recordHeaders: false,
            recordBody: false
        }
    }
});

// Advanced configuration
posthog.init('token', {
    session_recording: {
        enabled: true,
        sampleRate: 0.1, // Record 10% of sessions
        minimumDurationMilliseconds: 5000, // Only record sessions > 5s
        linkedFlag: 'record-sessions', // Require feature flag
        recordCanvas: true,
        recordCrossOriginIframes: true,
        networkPayloadCapture: {
            recordHeaders: {
                request: true,
                response: false
            },
            recordBody: {
                request: ['application/json'],
                response: ['application/json']
            }
        }
    }
});

// Disable session recording
posthog.init('token', {
    disable_session_recording: true
});
```

### Privacy Masking

Configure what data to mask in recordings:

```typescript { .api }
posthog.init('token', {
    session_recording: {
        // Mask all inputs (passwords, credit cards, etc.)
        maskAllInputs: true,

        // Mask specific text elements
        maskTextSelector: '.user-name, .email, .phone',

        // Block elements completely (won't appear in recording)
        blockSelector: '.sensitive-section, .payment-info',

        // Custom network masking
        maskCapturedNetworkRequestFn: (data) => {
            // Remove sensitive headers
            if (data.headers) {
                delete data.headers['Authorization'];
                delete data.headers['Cookie'];
            }

            // Mask sensitive URLs
            if (data.url.includes('/api/secret')) {
                return null; // Don't capture this request
            }

            return data;
        }
    }
});
```

## Best Practices

### Respect User Privacy

Always consider privacy when recording sessions:

```typescript
// ✅ Good: Get user consent
function requestRecordingConsent() {
    const consent = confirm('May we record your session to improve our service?');

    if (consent) {
        posthog.startSessionRecording();
        localStorage.setItem('recording_consent', 'granted');
    }
}

// ✅ Good: Stop recording on sensitive pages
router.on('route', (route) => {
    const sensitivePaths = ['/payment', '/settings/security', '/admin'];

    if (sensitivePaths.some(path => route.path.includes(path))) {
        posthog.stopSessionRecording();
    }
});

// ✅ Good: Mask sensitive data
posthog.init('token', {
    session_recording: {
        maskAllInputs: true,
        blockSelector: '.payment-form, .ssn, .credit-card'
    }
});
```

### Conditional Recording

Record sessions based on specific conditions:

```typescript
// Record only for authenticated users
if (user.isAuthenticated) {
    posthog.startSessionRecording();
}

// Record based on plan
if (user.plan === 'enterprise' || user.plan === 'pro') {
    posthog.startSessionRecording();
}

// Record when feature flag is enabled
posthog.onFeatureFlags((flags) => {
    if (flags['enable-session-recording']) {
        posthog.startSessionRecording();
    }
});

// Sample recording (10% of sessions)
if (Math.random() < 0.1) {
    posthog.startSessionRecording();
}
```

### Error Context

Include session replay URLs in error reports:

```typescript
// Add replay to error tracking
window.addEventListener('error', (event) => {
    const replayUrl = posthog.get_session_replay_url({
        withTimestamp: true,
        timestampLookBack: 10
    });

    logError({
        message: event.message,
        stack: event.error?.stack,
        replayUrl: replayUrl,
        sessionId: posthog.get_session_id()
    });
});

// Include in support tickets
function submitSupportTicket(description) {
    const ticket = {
        description: description,
        sessionReplay: posthog.get_session_replay_url({
            withTimestamp: true
        }),
        sessionId: posthog.get_session_id(),
        userId: posthog.get_distinct_id()
    };

    sendToSupport(ticket);
}
```

### Performance Considerations

Optimize recording for performance:

```typescript
// Only record sessions longer than threshold
posthog.init('token', {
    session_recording: {
        minimumDurationMilliseconds: 10000 // 10 seconds
    }
});

// Disable canvas recording if not needed
posthog.init('token', {
    session_recording: {
        recordCanvas: false // Better performance
    }
});

// Limit network capture
posthog.init('token', {
    session_recording: {
        networkPayloadCapture: {
            recordHeaders: false,
            recordBody: ['application/json'] // Only JSON bodies
        }
    }
});
```

## Common Patterns

### Support Integration

Integrate session replays with customer support:

```typescript
function CustomerSupportWidget() {
    const [sessionUrl, setSessionUrl] = useState('');

    useEffect(() => {
        // Get replay URL when widget opens
        const url = posthog.get_session_replay_url({
            withTimestamp: true
        });
        setSessionUrl(url);
    }, []);

    function sendMessage(message) {
        submitToSupport({
            message: message,
            sessionReplay: sessionUrl,
            sessionId: posthog.get_session_id(),
            userEmail: user.email
        });
    }

    return (
        <div>
            <textarea onChange={e => setMessage(e.target.value)} />
            <button onClick={() => sendMessage(message)}>
                Send (includes session replay)
            </button>
        </div>
    );
}
```

### Error Tracking Integration

Link errors to session replays:

```typescript
// Sentry integration
Sentry.init({
    beforeSend(event) {
        // Add session replay URL to Sentry events
        event.contexts = {
            ...event.contexts,
            posthog: {
                session_id: posthog.get_session_id(),
                replay_url: posthog.get_session_replay_url({
                    withTimestamp: true
                })
            }
        };
        return event;
    }
});

// Custom error handler
class ErrorHandler {
    captureError(error) {
        const errorReport = {
            message: error.message,
            stack: error.stack,
            timestamp: Date.now(),
            sessionReplay: posthog.get_session_replay_url({
                withTimestamp: true,
                timestampLookBack: 30
            }),
            sessionId: posthog.get_session_id()
        };

        this.sendToBackend(errorReport);
    }
}
```

### Conditional Recording by Route

Record specific pages or flows:

```typescript
// Record only important user flows
const recordedRoutes = [
    '/checkout',
    '/onboarding',
    '/settings',
    '/dashboard'
];

router.on('route', (route) => {
    const shouldRecord = recordedRoutes.some(r =>
        route.path.startsWith(r)
    );

    if (shouldRecord && !posthog.sessionRecordingStarted()) {
        posthog.startSessionRecording();
    } else if (!shouldRecord && posthog.sessionRecordingStarted()) {
        posthog.stopSessionRecording();
    }
});
```

### Session Replay in User Research

Use recordings for user research and testing:

```typescript
// Tag test sessions
posthog.capture('user_test_started', {
    test_id: 'checkout-flow-test-v2',
    participant_id: 'P001'
});

posthog.startSessionRecording();

// Track test milestones
function trackTestStep(stepName) {
    posthog.capture('user_test_step', {
        step: stepName,
        session_id: posthog.get_session_id()
    });
}

// End test with replay link
function endUserTest() {
    const replayUrl = posthog.get_session_replay_url();

    posthog.capture('user_test_completed', {
        replay_url: replayUrl
    });

    posthog.stopSessionRecording();
}
```

### A/B Test Recordings

Record sessions for experiment analysis:

```typescript
// Record variant exposure
posthog.onFeatureFlags((flags) => {
    const variant = flags['checkout-experiment'];

    if (variant) {
        // Tag session with variant
        posthog.register({
            experiment_variant: variant
        });

        // Start recording for experiment analysis
        posthog.startSessionRecording();

        posthog.capture('experiment_exposure', {
            experiment: 'checkout-experiment',
            variant: variant,
            session_id: posthog.get_session_id()
        });
    }
});
```

### Rage Click Detection

Detect and record frustrated user interactions:

```typescript
let clickCount = 0;
let clickTimer = null;
const RAGE_CLICK_THRESHOLD = 5;
const RAGE_CLICK_WINDOW = 2000; // 2 seconds

document.addEventListener('click', (event) => {
    clickCount++;

    if (clickTimer) {
        clearTimeout(clickTimer);
    }

    if (clickCount >= RAGE_CLICK_THRESHOLD) {
        // Rage click detected
        console.log('Rage click detected!');

        // Ensure recording is active
        if (!posthog.sessionRecordingStarted()) {
            posthog.startSessionRecording();
        }

        // Capture event with replay
        posthog.capture('rage_click_detected', {
            element: event.target.tagName,
            click_count: clickCount,
            replay_url: posthog.get_session_replay_url({
                withTimestamp: true,
                timestampLookBack: 10
            })
        });

        clickCount = 0;
    }

    clickTimer = setTimeout(() => {
        clickCount = 0;
    }, RAGE_CLICK_WINDOW);
});
```

## Advanced Recording Patterns

### Conditional Recording Based on User State

```typescript { .api }
// Record based on multiple conditions
function shouldRecordSession(user: User, page: string): boolean {
    // Don't record for internal users
    if (user.email?.endsWith('@company.com')) {
        return false;
    }
    
    // Record premium users
    if (user.plan === 'premium' || user.plan === 'enterprise') {
        return true;
    }
    
    // Record important flows for all users
    const importantFlows = ['/checkout', '/onboarding', '/payment'];
    if (importantFlows.some(flow => page.startsWith(flow))) {
        return true;
    }
    
    // Sample other sessions (10%)
    return Math.random() < 0.1;
}

// Apply conditional recording
if (shouldRecordSession(currentUser, window.location.pathname)) {
    posthog.startSessionRecording();
}
```

### Recording with Feature Flags

```typescript { .api }
// Use feature flags to control recording
posthog.onFeatureFlags((flags) => {
    const recordingConfig = posthog.getFeatureFlagPayload('recording-config') as {
        enabled: boolean;
        sampleRate: number;
        maskInputs: boolean;
    } | undefined;
    
    if (recordingConfig?.enabled) {
        if (Math.random() < recordingConfig.sampleRate) {
            posthog.set_config({
                session_recording: {
                    enabled: true,
                    maskAllInputs: recordingConfig.maskInputs
                }
            });
            
            posthog.startSessionRecording();
        }
    }
});
```

### Network Payload Filtering

```typescript { .api }
// Advanced network request filtering
posthog.init('token', {
    session_recording: {
        enabled: true,
        networkPayloadCapture: {
            recordHeaders: {
                request: true,
                response: false
            },
            recordBody: {
                request: ['application/json'],
                response: ['application/json']
            }
        },
        maskCapturedNetworkRequestFn: (request) => {
            // Don't capture authentication requests
            if (request.url.includes('/auth/') || request.url.includes('/login')) {
                return null;
            }
            
            // Mask sensitive headers
            if (request.headers) {
                const maskedHeaders = { ...request.headers };
                delete maskedHeaders['Authorization'];
                delete maskedHeaders['Cookie'];
                delete maskedHeaders['X-API-Key'];
                request.headers = maskedHeaders;
            }
            
            // Mask sensitive response fields
            if (request.responseBody) {
                try {
                    const body = JSON.parse(request.responseBody);
                    if (body.token) body.token = '[MASKED]';
                    if (body.password) body.password = '[MASKED]';
                    if (body.credit_card) body.credit_card = '[MASKED]';
                    request.responseBody = JSON.stringify(body);
                } catch (e) {
                    // Not JSON, skip masking
                }
            }
            
            return request;
        }
    }
});
```

## Edge Cases & Error Handling

### Recording Initialization Failure

```typescript { .api }
// Handle recording failure gracefully
function startRecordingSafely() {
    try {
        if (posthog.sessionRecording) {
            posthog.startSessionRecording();
            
            // Verify it started
            setTimeout(() => {
                if (!posthog.sessionRecordingStarted()) {
                    console.warn('Session recording failed to start');
                    // Log to monitoring service
                    logWarning('session_recording_failed');
                }
            }, 1000);
        }
    } catch (error) {
        console.error('Error starting session recording:', error);
    }
}
```

### Handle Session Transitions

```typescript { .api }
// Handle session ID changes
let currentSessionId = posthog.get_session_id();

posthog.onSessionId((newSessionId, windowId) => {
    console.log('Session changed:', {
        old: currentSessionId,
        new: newSessionId,
        windowId: windowId
    });
    
    // Track session transition
    posthog.capture('session_transitioned', {
        previous_session: currentSessionId,
        new_session: newSessionId,
        window_id: windowId
    });
    
    currentSessionId = newSessionId;
    
    // Restart recording for new session if needed
    if (shouldRecordCurrentPage()) {
        posthog.startSessionRecording();
    }
});
```

### Memory Management

```typescript { .api }
// Prevent memory leaks with long-running recordings
class RecordingManager {
    private maxRecordingTime = 30 * 60 * 1000; // 30 minutes
    private recordingTimer?: NodeJS.Timeout;
    
    startRecording() {
        posthog.startSessionRecording();
        
        // Stop after max time
        this.recordingTimer = setTimeout(() => {
            console.log('Max recording time reached, restarting');
            this.restartRecording();
        }, this.maxRecordingTime);
    }
    
    stopRecording() {
        if (this.recordingTimer) {
            clearTimeout(this.recordingTimer);
        }
        posthog.stopSessionRecording();
    }
    
    restartRecording() {
        this.stopRecording();
        // Wait briefly before restarting
        setTimeout(() => {
            this.startRecording();
        }, 1000);
    }
}
```

## Performance Optimization

### Minimize Recording Overhead

```typescript { .api }
// Disable expensive features if not needed
posthog.init('token', {
    session_recording: {
        enabled: true,
        // Disable canvas recording (expensive)
        recordCanvas: false,
        // Disable console log recording if not needed
        consoleLogRecordingEnabled: false,
        // Minimal network capture
        networkPayloadCapture: {
            recordHeaders: false,
            recordBody: false
        },
        // Don't capture performance (if not needed)
        capturePerformance: false
    }
});
```

### Adaptive Recording Quality

```typescript { .api }
// Adjust recording based on device performance
function getRecordingConfig(): SessionRecordingOptions {
    const isLowEndDevice = (
        navigator.deviceMemory && navigator.deviceMemory < 4 ||
        navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4
    );
    
    if (isLowEndDevice) {
        return {
            enabled: true,
            recordCanvas: false,
            consoleLogRecordingEnabled: false,
            networkPayloadCapture: undefined,
            capturePerformance: false
        };
    }
    
    return {
        enabled: true,
        recordCanvas: true,
        consoleLogRecordingEnabled: true,
        capturePerformance: true
    };
}

posthog.init('token', {
    session_recording: getRecordingConfig()
});
```

## Troubleshooting

### Recording Not Starting

```typescript { .api }
// Debug recording issues
function debugRecording() {
    console.log('Recording started:', posthog.sessionRecordingStarted());
    console.log('Recording disabled:', posthog.config.disable_session_recording);
    console.log('Recording config:', posthog.config.session_recording);
    console.log('Session ID:', posthog.get_session_id());
    
    // Check if sessionRecording API is available
    console.log('Recording API available:', !!posthog.sessionRecording);
    
    if (posthog.sessionRecording) {
        console.log('Recording started:', posthog.sessionRecording.started);
    }
    
    // Try force starting
    console.log('Force starting...');
    posthog.startSessionRecording(true);
    
    setTimeout(() => {
        console.log('Recording started after force:', posthog.sessionRecordingStarted());
    }, 1000);
}
```

### Replay URL Not Generated

```typescript { .api }
// Verify replay URL generation
function getReplayUrlSafely(): string | null {
    if (!posthog.sessionRecordingStarted()) {
        console.warn('Recording not started, cannot get replay URL');
        return null;
    }
    
    try {
        const url = posthog.get_session_replay_url({
            withTimestamp: true
        });
        
        if (!url || url.includes('undefined')) {
            console.warn('Invalid replay URL generated:', url);
            return null;
        }
        
        return url;
    } catch (error) {
        console.error('Error generating replay URL:', error);
        return null;
    }
}
```

### Recording Performance Impact

```typescript { .api }
// Monitor recording performance impact
class RecordingPerformanceMonitor {
    private metrics = {
        memoryBefore: 0,
        memoryAfter: 0,
        startTime: 0
    };
    
    start() {
        this.metrics.memoryBefore = (performance as any).memory?.usedJSHeapSize || 0;
        this.metrics.startTime = Date.now();
        
        posthog.startSessionRecording();
        
        // Check impact after 10 seconds
        setTimeout(() => this.check(), 10000);
    }
    
    check() {
        this.metrics.memoryAfter = (performance as any).memory?.usedJSHeapSize || 0;
        const memoryIncrease = this.metrics.memoryAfter - this.metrics.memoryBefore;
        const duration = Date.now() - this.metrics.startTime;
        
        console.log('Recording performance:', {
            duration_ms: duration,
            memory_increase_mb: memoryIncrease / 1024 / 1024,
            memory_rate_mb_per_min: (memoryIncrease / 1024 / 1024) / (duration / 60000)
        });
        
        // Stop if memory usage is too high
        if (memoryIncrease > 50 * 1024 * 1024) {  // 50 MB
            console.warn('Recording using too much memory, stopping');
            posthog.stopSessionRecording();
        }
    }
}
```

## See Also

- [Initialization](./initialization.md) - Configure session recording
- [Privacy](./privacy.md) - Privacy and consent for recordings
- [Error Tracking](./error-tracking.md) - Link errors to session replays
- [Feature Flags](./feature-flags.md) - Control recording with flags
- [Types](./types.md) - Session recording type definitions
