Skip to content

Replaying recordings with rrweb Cloud

This guide shows you how to fetch and replay recorded recordings from rrweb Cloud using rrweb's replay functionality.

You can view and manage your recordings in the rrweb Cloud dashboard.

Installing the rrweb player

For a pre-built player UI, install rrweb-player: (recommended)

bash
npm install rrweb-player

For a bare bones player without UI, install the rrweb replay package:

bash
npm install @rrweb/replay

CDN Installation

Include rrweb-player via CDN for quick setup:

html
<link
  rel="stylesheet"
  href="https://cdn.rrweb.com/rrweb-player/next/style.css"
/>
<script type="module">
  import rrwebPlayer from 'https://cdn.rrweb.com/rrweb-player/next/rrweb-player.js';
</script>

Basic Replay Setup

Fetch Events from rrweb Cloud

First, retrieve the recorded events for a recording. You can find your recording IDs in the rrweb Cloud dashboard.

javascript
async function fetchRecordingEvents(recordingId) {
  try {
    const response = await fetch(
      `https://api.rrweb.com/recordings/${recordingId}/events`,
    );

    if (!response.ok) {
      throw new Error(`Failed to fetch events: ${response.status}`);
    }

    const events = await response.json();
    return events;
  } catch (error) {
    console.error('Error fetching recording events:', error);
    return null;
  }
}

If your player also needs the recording descriptor and metadata, request embedded events from the recording detail endpoint:

javascript
async function fetchRecordingWithEvents(recordingId) {
  const response = await fetch(
    `https://api.rrweb.com/recordings/${recordingId}?includeEvents=true`,
  );

  if (!response.ok) {
    throw new Error(`Failed to fetch recording: ${response.status}`);
  }

  const recording = await response.json();
  return {
    metadata: recording.metadata,
    events: recording.events,
  };
}

The /recordings/{recordingId}/events endpoint remains the canonical event-stream endpoint. includeEvents=true is a convenience for replay views that need metadata and events together; descriptor responses stay lightweight by default.

Initialize and Play Replayer

Once you have the events, initialize the player and play the recording:

javascript
// Fetch events
const events = await fetchRecordingEvents(recordingId);

if (events && events.length > 0) {
  // Initialize rrweb-player
  const player = new rrwebPlayer({
    target: document.getElementById('player-container'), // Container element
    props: {
      events: events,
      // Player options
      width: 1024, // Player width (px)
      height: 576, // Player height (px)
      maxScale: 1, // Maximum scale (1 = 100%, 0 = unlimited)
      autoPlay: true, // Auto-start playback
      speedOption: [1, 2, 4, 8], // Available speed options
      showController: true, // Show player controls
      tags: {}, // Custom event tag styles
      // All other options are passed to the underlying replayer
      skipInactive: true,
      mouseTail: true,
      blockClass: 'rr-block',
    },
  });
}

Player Endpoint

For a quick preview without writing any code, you can use the built-in player endpoint:

https://api.rrweb.com/recordings/{recordingId}/player.html

This endpoint serves an HTML page with the rrweb player pre-configured. Simply replace {recordingId} with your recording ID and open the URL in a browser.

Example:

https://api.rrweb.com/recordings/550e8400-e29b-41d4-a716-446655440000/player.html

The player endpoint supports these query parameters:

  • controls=true|false to show or hide the control bar
  • autoplay=true|false to start playback automatically
  • mode=standard|minimal|capture to switch between the full page, stripped embed layout, and full-viewport capture layout
  • speed=0.25-4 to set the playback speed
  • time=offset:0|offset:-5000|percent:50|epoch:1702900000000 to start at a specific point in the recording

The player page includes:

  • Full playback controls (play, pause, speed adjustment, timeline scrubbing)
  • Mouse trail visualization
  • Event fetching from the recording events endpoint
  • Skip inactive periods
  • Responsive layout that adapts to your screen size

This is useful for quick debugging, sharing recordings with team members, or using the player HTML as the basis for screenshots and future export endpoints.

Screenshot Endpoint

To capture a still image of a recording at a specific playback position, use the screenshot endpoint:

https://api.rrweb.com/recordings/{recordingId}/screenshot.png

Supported formats are .png, .jpeg, and .webp. You can also negotiate the format with the Accept header when no file extension is provided.

Supported query parameters:

  • time=offset:0|offset:-5000|percent:50|epoch:1702900000000
  • width=1-4096
  • height=1-4096
  • quality=1-100 for JPEG and WebP output

Alternative: Using the rrweb replay package

If you prefer to build your own player UI, you can use the rrweb replay package:

bash
npm install @rrweb/replay
javascript
// Fetch events for a specific recording
const recordingId = 'your-recording-id';
const events = await fetchRecordingEvents(recordingId);

if (events && events.length > 0) {
  // Initialize the replayer
  const replayer = new rrweb.Replayer(events, {
    // Configuration options
    speed: 1, // Playback speed (1 = normal)
    root: document.body, // Root element for replay
    loadTimeout: 0, // Timeout for loading external resources
    skipInactive: false, // Skip inactive periods
    inactivePeriodThreshold: 10000, // Threshold for inactive periods (ms)
    showWarning: true, // Show warning messages
    showDebug: false, // Show debug messages
    blockClass: 'rr-block', // Class for blocked elements
    liveMode: false, // Enable live mode
    insertStyleRules: [], // Custom CSS rules to inject
    triggerFocus: true, // Trigger focus events
    UNSAFE_replayCanvas: false, // Replay canvas elements (unsafe)
    pauseAnimation: true, // Pause CSS animations when paused
    mouseTail: true, // Show mouse cursor trail
  });

  // Start playback
  replayer.play();
}

Advanced Replay Features

Live Mode Replay (not supported by rrweb Cloud yet)

For real-time or near real-time replay:

javascript
// Initialize in live mode
const replayer = new rrweb.Replayer(events, {
  liveMode: true,
  speed: 1,
});

// For existing events, play with offset
if (events.length > 0) {
  const offset = Date.now() - events[0].timestamp;
  replayer.play(offset);
}

// For true live streaming, start with empty events array
const liveReplayer = new rrweb.Replayer([], {
  liveMode: true,
});
liveReplayer.startLive();

// Then add events as they arrive
function addLiveEvent(event) {
  liveReplayer.addEvent(event);
}

Control Playback

Control playback programmatically:

javascript
// Play from the beginning
replayer.play();

// Play from a specific time (in milliseconds)
replayer.play(3000); // Start at 3 seconds

// Pause playback
replayer.pause();

// Pause at a specific time
replayer.pause(5000); // Pause at 5 seconds

// Resume playback
replayer.play();

// Jump to a specific time
replayer.goto(10000); // Jump to 10 seconds

// Get current time
const currentTime = replayer.getCurrentTime();

// Get total duration
const totalDuration = replayer.getMetaData().totalTime;

// Destroy the replayer
replayer.destroy();

Custom Event Handling

Handle custom events during replay:

javascript
const replayer = new rrweb.Replayer(events, {
  // Custom event handlers
  hooks: {
    // Called before replaying an event
    beforeReplay: (event) => {
      console.log('Replaying event:', event);
    },

    // Called after replaying an event
    afterReplay: (event) => {
      if (event.type === 5 && event.data.tag === 'user-action') {
        // Handle custom user action events
        console.log('User action:', event.data.payload);
      }
    },
  },
});

Replayer Configuration Options

Privacy and Security

javascript
const replayer = new rrweb.Replayer(events, {
  // Block sensitive elements
  blockClass: 'rr-block',
  blockSelector: '.sensitive, .private-data',

  // Mask text in sensitive elements
  maskTextClass: 'rr-mask',
  maskTextSelector: '.email, .phone, .ssn',

  // Don't trigger focus (prevents stealing focus)
  triggerFocus: false,

  // Don't replay canvas (safer)
  UNSAFE_replayCanvas: false,
});

Custom Styling

javascript
const replayer = new rrweb.Replayer(events, {
  // Inject custom CSS
  insertStyleRules: [
    '.replayer-wrapper { background: #f0f0f0; }',
    '.replayer-mouse { cursor: none; }',
    '.rr-block { background: #ccc; }',
  ],
});

Using Plugins

Install and Use Replay Plugins

bash
npm install @rrweb/rrweb-plugin-sequential-id-replay
javascript
import rrweb from 'rrweb';
import { sequentialIdReplayPlugin } from '@rrweb/rrweb-plugin-sequential-id-replay';

// Initialize with plugin
const replayer = new rrweb.Replayer(events, {
  plugins: [
    sequentialIdReplayPlugin({
      key: 'sequenceId',
    }),
  ],
});

Custom Replay Plugin

Create a custom plugin to handle specific events:

javascript
// Define a custom replay plugin
const customReplayPlugin = {
  handler(event, isSync, context) {
    if (event.type === rrweb.EventType.Plugin) {
      // Handle custom plugin events
      if (event.data.plugin === 'my-custom-plugin') {
        console.log('Custom plugin data:', event.data.payload);

        // Access replayer context
        const { replayer, mirror } = context;

        // Perform custom actions
        const element = mirror.getNode(event.data.payload.nodeId);
        if (element) {
          element.classList.add('highlighted');
        }
      }
    }
  },
};

// Use the plugin
const replayer = new rrweb.Replayer(events, {
  plugins: [customReplayPlugin],
});

Handle Large Recordings

Coming later...

Filtering recordings by metadata

When listing recordings via GET /recordings, you can filter by metadata using the metadata[key]=value deep object syntax. Always wrap both the key and value in encodeURIComponent before building the query string so filters containing spaces or punctuation still match the stored metadata.

ts
const params = new URLSearchParams();
params.set(
  `metadata[${encodeURIComponent('user id')}]`,
  encodeURIComponent(currentUser.id),
);

const response = await fetch(
  `https://api.rrweb.com/recordings?${params.toString()}`,
);

Troubleshooting

  • Events not loading: Check recording ID and ensure events were ingested successfully
  • Replay not starting: Verify events array is not empty and contains valid rrweb events
  • Styling issues: Ensure rrweb CSS is loaded and no conflicting styles
  • Canvas not replaying: Enable UNSAFE_replayCanvas: true (note: this removes sandbox security)

Next Steps