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 ();
}
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:
Navigate to Sessions
Click on any session to view recording
Use playback controls:
Play/Pause
Speed control (0.5x - 4x)
Skip inactivity
Jump to events
View console logs
Inspect network
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
Recordings not appearing in dashboard
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
}
Sensitive data appearing in recordings
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
Legal Considerations
Always comply with privacy laws like GDPR, CCPA, and others. Session recording may require explicit user consent in many jurisdictions.
Compliance Checklist
Next Steps