Skip to main content
Session recording uses rrweb under the hood and is lazy-loaded only when enabled, keeping your bundle size small (~5KB base + ~200KB when recording).

What is Session Recording?

Session recording captures all user interactions on your website or app, allowing you to replay sessions exactly as users experienced them. This includes:
  • Mouse movements and clicks
  • Keyboard input (with privacy options)
  • Page scrolls and navigation
  • DOM changes and animations
  • Console logs and errors
  • Network requests (optional)

Quick Start

Enable session recording with a single configuration option:
import composite from '@composite-inc/composite-js';

await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: true  // That's it!
});
The SDK automatically:
  • Lazy-loads recording libraries (~200KB)
  • Starts capturing user sessions
  • Handles compression and batching
  • Manages privacy and security

Configuration Options

Basic Configuration

await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Privacy settings
    maskAllInputs: true,           // Mask all input fields
    maskTextSelector: '.sensitive', // Mask specific text elements
    blockSelector: '.no-record',    // Don't record these elements

    // Performance settings
    compress: true,                 // Enable compression (recommended)
    sampling: {
      canvas: 50,                   // Sample canvas at 50fps
      input: 'last',                // Record last input value
      media: 800                    // Sample media every 800ms
    },

    // Recording options
    recordCanvas: false,            // Disable canvas recording
    recordCrossOriginIframes: false, // Don't record cross-origin iframes
    collectFonts: true,             // Collect font faces

    // Advanced options
    slimDOMOptions: {
      script: true,                // Don't record script tags
      comment: true,               // Don't record comments
      headFavicon: true,           // Don't record favicon
      headWhitespace: true,        // Don't record head whitespace
      headMetaDescKeywords: true   // Don't record meta keywords
    }
  }
});

Privacy-First Recording

Protect sensitive user data with multiple privacy options:
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Mask all text input by default
    maskAllInputs: true,

    // Mask specific elements by CSS selector
    maskTextSelector: '.password, .credit-card, [data-sensitive]',

    // Completely block elements from recording
    blockSelector: '.privacy-block, #sensitive-data',

    // Custom masking function
    maskInputFn: (text, element) => {
      if (element.type === 'password') {
        return '********';
      }
      if (element.classList.contains('email')) {
        return text.replace(/(.{2}).*(@.*)/, '$1***$2');
      }
      return text;
    },

    // Ignore specific input types
    ignoreInputTypes: ['password', 'email', 'tel'],

    // Custom privacy rules
    privacyOptions: {
      maskAllText: false,
      maskImages: false,
      unmaskTextSelectors: ['.public-content']
    }
  }
});
Always mask sensitive information like passwords, credit card numbers, SSNs, and health data. Use the blockSelector option to completely exclude sensitive areas from recordings.

Manual Control

Start and Stop Recording

Control recording programmatically:
// Start recording manually
composite.startSessionRecording();

// Stop recording
composite.stopSessionRecording();

// Check if recording is active
const isRecording = composite.isSessionRecordingActive();

// Pause and resume
composite.pauseSessionRecording();
composite.resumeSessionRecording();

Conditional Recording

Record only specific user segments or pages:
// Record only for specific users
const user = await getCurrentUser();
if (user.plan === 'premium' || user.isInternalUser) {
  composite.startSessionRecording();
}

// Record only specific pages
if (window.location.pathname.startsWith('/checkout')) {
  composite.startSessionRecording();
}

// Record based on feature flag
if (featureFlags.sessionRecordingEnabled) {
  composite.startSessionRecording();
}

Performance Optimization

Sampling Strategies

Reduce performance impact with sampling:
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Record only 50% of sessions
    sampleRate: 0.5,

    // Or use custom sampling logic
    shouldRecord: () => {
      // Record 10% of mobile users
      if (isMobile()) return Math.random() < 0.1;
      // Record 100% of specific pages
      if (isCheckoutPage()) return true;
      // Record 30% of other sessions
      return Math.random() < 0.3;
    }
  }
});

Network Optimization

Configure batching and compression:
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Compression (reduces size by ~70%)
    compress: true,
    compressOptions: {
      level: 6  // 0-9, higher = better compression
    },

    // Batching configuration
    batchConfig: {
      flushInterval: 2000,    // Send every 2 seconds
      maxBufferSize: 5000000, // 5MB max buffer
      maxBatchSize: 7000000,  // 7MB max batch size
    },

    // Network retry
    networkOptions: {
      retryCount: 3,
      retryDelay: 1000,
      timeout: 30000
    }
  }
});

DOM Optimization

Reduce recording overhead:
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Don't record static elements
    recordCanvas: false,
    recordMedia: false,
    recordFonts: false,

    // Simplify DOM capture
    slimDOMOptions: {
      script: true,
      comment: true,
      headFavicon: true,
      headWhitespace: true,
      headMetaDescKeywords: true,
      headMetaSocial: true,
      headMetaRobots: true,
      headMetaHttpEquiv: true,
      headMetaAuthorship: true,
      headMetaVerification: true
    },

    // Reduce mutation observation
    mutationLimit: 500,  // Stop after 500 mutations per batch
    moveLimit: 500       // Stop after 500 position changes
  }
});

Advanced Features

Custom Events in Recordings

Add custom events to session timeline:
// Add custom event to recording
composite.addCustomRecordingEvent('user_frustrated', {
  rage_clicks: 5,
  time_on_page: 120
});

// Mark important moments
composite.addRecordingAnnotation('User encountered error', {
  error_code: 'PAYMENT_FAILED',
  timestamp: Date.now()
});

Error Tracking

Automatically capture errors in recordings:
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Capture console logs and errors
    recordLog: true,
    logOptions: {
      level: ['info', 'warn', 'error'],
      lengthThreshold: 1000,
      stringifyOptions: {
        stringLengthLimit: 1000,
        depthLimit: 3
      }
    },

    // Capture network errors
    recordNetwork: true,
    networkOptions: {
      initiatorTypes: ['fetch', 'xmlhttprequest'],
      recordHeaders: false,  // Don't record sensitive headers
      recordBody: false      // Don't record request/response bodies
    }
  }
});

Cross-Domain Recording

Record across multiple domains:
// Main domain configuration
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    crossOriginIsolation: true,
    recordCrossOriginIframes: true,

    // Whitelist trusted domains
    iframeWhitelist: [
      'https://checkout.example.com',
      'https://cdn.example.com'
    ]
  }
});

// In iframe (must also initialize)
if (window !== window.top) {
  await composite.init({
    apiKey: 'pk_your_api_key',
    sessionRecording: {
      recordAsChildFrame: true,
      parentOrigin: 'https://main.example.com'
    }
  });
}

Session Recording Data

What’s Captured

Each recording includes:
{
  session_id: "sess_abc123",
  recording_id: "rec_xyz789",
  start_time: "2024-01-15T10:00:00Z",
  end_time: "2024-01-15T10:05:00Z",
  duration_ms: 300000,

  events: [
    // User interactions
    { type: 'click', timestamp: 1234, target: '.button' },
    { type: 'input', timestamp: 2345, target: '#email' },
    { type: 'scroll', timestamp: 3456, position: 500 },

    // Page changes
    { type: 'navigation', timestamp: 4567, url: '/checkout' },
    { type: 'resize', timestamp: 5678, width: 1200, height: 800 },

    // Errors
    { type: 'error', timestamp: 6789, message: 'Network error' }
  ],

  metadata: {
    browser: "Chrome 120",
    os: "Mac OS X",
    viewport: { width: 1920, height: 1080 },
    url: "https://app.example.com"
  }
}

Playback in Dashboard

View recordings in the Analytics Dashboard:
  1. Navigate to Sessions
  2. Click on any session to view recording
  3. Use playback controls:
    • Play/Pause
    • Speed control (0.5x - 4x)
    • Skip inactivity
    • Jump to events
    • View console logs
    • Inspect network

Platform-Specific Setup

Single Page Applications (SPAs)

// React/Vue/Angular
await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Capture route changes
    recordHistory: true,

    // Handle dynamic content
    mutationObserver: {
      childList: true,
      attributes: true,
      subtree: true
    }
  }
});

// Manual route tracking
router.afterEach((to) => {
  composite.addCustomRecordingEvent('route_changed', {
    path: to.path,
    name: to.name
  });
});

Chrome Extensions

See our dedicated Chrome Extension Guide for extension-specific setup.

Mobile Web

Optimize for mobile devices:
const isMobile = /Mobile|Android|iPhone/i.test(navigator.userAgent);

await composite.init({
  apiKey: 'pk_your_api_key',
  sessionRecording: {
    // Lower sample rate on mobile
    sampleRate: isMobile ? 0.1 : 0.5,

    // Disable expensive features
    recordCanvas: !isMobile,
    recordMedia: !isMobile,

    // More aggressive compression
    compress: true,
    compressOptions: {
      level: isMobile ? 9 : 6
    }
  }
});

Troubleshooting

Check:
  • Session recording is enabled: sessionRecording: true
  • API key is correct and has recording permissions
  • No JavaScript errors in console
  • Network tab shows requests to /ingest/s
  • User has not blocked tracking
Optimize performance:
sessionRecording: {
  recordCanvas: false,
  recordMedia: false,
  sampleRate: 0.3,
  mutationLimit: 200,
  compress: true
}
Add privacy controls:
sessionRecording: {
  maskAllInputs: true,
  maskTextSelector: '.sensitive, [data-private]',
  blockSelector: '#credit-card-form',
  recordNetwork: false
}
Enable compression and filtering:
sessionRecording: {
  compress: true,
  slimDOMOptions: { /* ... */ },
  recordCanvas: false,
  maxBatchSize: 5000000
}

Best Practices

Respect Privacy

Always mask sensitive data and get user consent

Optimize Performance

Use sampling and disable unnecessary features

Test Thoroughly

Verify recordings work across browsers and devices

Monitor Usage

Track recording data usage and costs
Always comply with privacy laws like GDPR, CCPA, and others. Session recording may require explicit user consent in many jurisdictions.

Compliance Checklist

  • Obtain user consent before recording
  • Provide clear privacy policy
  • Mask all personal data
  • Allow users to opt-out
  • Delete recordings upon request
  • Implement data retention policies

Next Steps