What is rrweb in one paragraph?
rrweb is the de-facto open-source recorder for web session replay. It is a small JavaScript library (~50 KB gzipped for the recorder) that takes an initial DOM snapshot, then streams every change to that DOM as a list of typed events. A separate rrweb-player package consumes those events and rebuilds the original page inside an iframe so the recording can be scrubbed like a video — except it is a real DOM, not pixels.
Who created it and what is the license?
- Author: Yuyz0112 (Yanzhen Yu)
- First public release: 2018
- License: MIT
- Repository: github.com/rrweb-io/rrweb
- Written in: TypeScript
Because it is MIT-licensed, commercial vendors can embed it without restriction, which is why most of the session replay industry runs on top of it.
How does the recorder work at a high level?
The recorder has three responsibilities:
- Take a serializable snapshot of the current DOM, including computed styles for inline elements and the contents of stylesheets
- Subscribe to every source of change (DOM, input, mouse, scroll, viewport, network)
- Emit each change as a typed event onto a callback the host application provides
Sources of change are wired up via standard browser APIs:
MutationObserverfor DOM tree changesaddEventListenerforinput,click,scroll,mousemove,touchstart,selectionchange- A monkey-patched
CSSStyleSheet.prototype.insertRulefor stylesheet edits - Wrappers around
XMLHttpRequestandfetchfor network metadata MediaQueryListandResizeObserverfor viewport changes
How do you start recording in code?
The minimal example from the rrweb README:
import * as rrweb from "rrweb";
const events = [];
const stopFn = rrweb.record({
emit(event) {
events.push(event);
},
maskAllInputs: true,
maskInputOptions: { password: true },
blockClass: "rr-block",
ignoreClass: "rr-ignore",
});
// Later, ship `events` to your backend
// stopFn() to stop recording
The emit callback fires every time a new event is produced. In production you batch events, gzip them, and send them to your backend on a timer or on visibilitychange.
What event types does rrweb produce?
| Type | Description |
|---|---|
| 0 | DomContentLoaded |
| 1 | Load |
| 2 | FullSnapshot — the initial serialized DOM tree |
| 3 | IncrementalSnapshot — a delta (mutation, input, scroll, mouse, etc.) |
| 4 | Meta — viewport size, URL |
| 5 | Custom — user-defined events |
| 6 | Plugin — events from rrweb plugins |
Type 3 is by far the most frequent and contains its own sub-source enum (Mutation, MouseInteraction, Scroll, Input, MouseMove, etc.).
How does the replayer work?
rrweb-player (or rrweb.Replayer directly) takes the array of events and:
- Creates a sandboxed
iframewithsandbox="allow-same-origin" - Rebuilds the FullSnapshot inside that iframe by setting
innerHTMLand re-attaching stylesheets - Walks forward through IncrementalSnapshots, applying each one in time order
- Renders a virtual cursor and visual click pulses on top
- Exposes
play,pause,goto(timestamp), and a timeline scrubber
Because it is a real DOM, you can right-click and inspect a replay in DevTools just like a live page.
Which products are built on rrweb?
- PostHog Session Replay
- Sentry Session Replay (forked variant)
- OpenReplay
- Highlight.io
- CloseTrace
- Many internal observability stacks at companies that built their own player
This makes rrweb's event format a de-facto standard — replays can often be moved between tools.
What about privacy?
rrweb ships with built-in privacy primitives:
maskAllInputsmasks every input valuemaskInputOptionslets you enable masking per input typeblockClass/blockSelectorskips entire subtreesignoreClassskips capturing inputs on matching elements- A
maskTextFncallback for custom redaction (PII, credit cards, etc.) - Password fields are masked unconditionally
How it relates to CloseTrace
CloseTrace uses rrweb for replay capture with browser-side privacy controls. Its lightweight core tracker records form, rage-click, and dead-click signals alongside the replay stream so those events can be shown in session evidence and Lead Recovery.