Session Replay
Record what a user saw and did in the browser, and play it back from the error, trace or session it belongs to.
Replay is part of @zharp/browser and works in web pages only — the Flutter, Android and iOS SDKs don't record. It is off until you turn it on. Recording uses rrweb: it captures the page's structure and its changes, not video.
Turn it on
The setup page's snippet records one session in ten from start to end, and every session that hits an error:
import { Zharp } from "@zharp/browser";
Zharp.init({
key: "zpk_…",
service: "checkout-web",
replay: {
sampleRate: 0.1, // record 10% of sessions from start to end
onErrorSampleRate: 1.0, // and every session that hits an error
},
});Sampling
| Option | Default | What it does |
|---|---|---|
sampleRate | 0 | Share of sessions recorded from start to end, 0 to 1. |
onErrorSampleRate | 0 | Share of the remaining sessions that keep the last minute in memory and upload it, with the rest of the session, only when an error happens. |
replay: {
sampleRate: 0.1, // record 10% of sessions from start to end
onErrorSampleRate: 1.0, // of the rest, keep the last minute in memory and upload it
// — and the rest of the session — only if an error happens
}- The decision is made once per session, when it starts.
- A session that won't record never downloads the recorder. One waiting for an error uploads nothing unless an error happens —
onErrorSampleRate: 1.0costs nothing for the sessions that go fine. - With the script tag, the recorder is a separate file,
zharp-replay.min.js, loaded from next tozharp.min.js;recorderUrlpoints elsewhere. Bundled builds load it as a chunk of their own.
Privacy
What people type into inputs, text areas and selects is masked. Turning that off with maskAllInputs: false sends passwords and card numbers unless blockSelector covers them.
| Option | Default | What it does |
|---|---|---|
maskAllInputs | true | Mask what is typed into every input, text area and select. |
maskAllText | false | Mask all text on the page, not just inputs. |
maskTextSelector | none | CSS selector for elements whose text is masked. |
blockSelector | none | CSS selector for elements left out of the recording entirely, such as a payment iframe. |
beforeSend | none | Called with each batch of events before it is sent; return false to drop it. |
replay: {
onErrorSampleRate: 1.0,
blockSelector: ".payment-form, [data-private]", // never recorded: space kept, contents left out
maskTextSelector: ".account-balance", // text replaced by placeholder characters
maskAllText: false, // true masks every text node on the page
maskAllInputs: true, // the default
}For anything really sensitive, prefer blockSelector: masked text is still recorded as placeholder characters of the right length, while a blocked element is never captured at all.
replay: {
onErrorSampleRate: 1.0,
beforeSend: (events) => !location.pathname.startsWith("/admin"), // false drops the batch
}Watching a replay
- On the Sessions tab, recorded sessions can be opened and played; the others say Not recorded. With errors narrows the list to the sessions worth watching.
- On an issue, Watch replay opens the recording of the session it happened in.
- Each recording lists the traces that happened during it, so you can go from a replay to the request that failed and back. The session's page also lists its requests, logs and errors in order.
Retention
Recordings are kept for 7 days, or for your plan's log retention if that is shorter. Deleting the app deletes its recordings. The Session replay item on the setup checklist ticks once the first recording arrives.