Session replay plugin
An opt-in plugin for the widget that attaches a rolling recording (the last 30 seconds by default) to each feedback submission. When a user reports a bug, you watch exactly what they did instead of asking for reproduction steps.
Recordings use rrweb and are gzipped in the browser via the native CompressionStream API. In the dashboard, each feedback with a replay gets a "Watch session replay" link that opens the player at the moment the feedback was given.
Install
rrweb ships inside the plugin chunk, so npm install @usero/sdk is the only install step. The plugin lives in a subpath export;
consumers who never import it pay zero rrweb bytes.
import { initUseroFeedbackWidget } from '@usero/sdk'
import { sessionReplay } from '@usero/sdk/plugins/session-replay'
initUseroFeedbackWidget({
clientId: 'YOUR_CLIENT_ID',
plugins: [
sessionReplay({
bufferSeconds: 30,
// Wait 3s of engagement before loading rrweb. If the user navigates
// away first, rrweb is never fetched.
startAfterMs: 3000,
// Record 50% of sessions. Decided once at init.
sampleRate: 0.5,
}),
],
})Even when the plugin is imported, rrweb itself lazy-loads at runtime via dynamic import() the first time the engagement gate
elapses, so opted-in consumers do not pay rrweb's bytes upfront either.
Options and privacy defaults
| Option | Default | What it does |
|---|---|---|
maskAllInputs |
true |
Mask <input> and <textarea> values in the recording. |
maskTextSelector |
'[data-usero-mask]' |
Mask text content of any node matching this selector. |
blockSelector |
'[data-usero-block]' |
Skip recording matching subtrees entirely. |
inlineStylesheet |
true |
Inline external stylesheets so replays render offline. |
sampling |
{ mousemove: 50, scroll: 100 } |
Throttle high-frequency events. |
bufferSeconds |
30 |
Length of the rolling in-memory buffer in seconds. |
startAfterMs |
0 |
Engagement gate in milliseconds before loading rrweb. |
sampleRate |
1 |
Probability (0 to 1) that a given session records at all. |
Masking sensitive content
Tag nodes at the source. Text inside data-usero-mask is masked; subtrees inside data-usero-block are never recorded:
<div data-usero-mask>jane@example.com</div>
<section data-usero-block>
<!-- billing details, never recorded -->
</section>Inputs and textareas are masked by default via maskAllInputs.
How it links to feedback
When a recording exists, the widget sends sessionReplayId and replayOffsetMs alongside the feedback submission. If you submit
feedback through the API yourself, those two fields are how a replay gets attached.