Session replay
Web and native mobile replay, masked on the device by default, in one player.
Replay is off until you turn it on. Web recordings use rrweb; iOS, Android and React Native recordings are native wireframes: the view tree, masked on the device, rendered as boxes and text by the same player. Nothing on mobile is captured as a screenshot.
Turn it on
import * as atrix from "@atrix.dev/analytics-web";
atrix.init("atx_pk_eu_production_XXXXXXXXXXXXXXXXXXXXXX", {
replay: {
sampleRate: 0.5, // half of sessions, decided per session id
minDurationMs: 2000, // shorter sessions are never uploaded
triggers: { events: ["checkout_started"] }, // record only sessions where this happens
},
});The recorder is a separate ~27 KB chunk, downloaded only when a session is actually recorded and only while
consent is granted. startReplay() and stopReplay() switch it at runtime.
Masking, per platform
Masking happens on the device, before anything is stored or sent. What is masked never leaves the phone or the browser.
Web
| Control | Effect |
|---|---|
| default | Every input value is masked. Password inputs are always masked, even inside data-atrix-unmask. |
maskAllText: true | Mask all page text. |
data-atrix-mask | Mask the text of a subtree. |
data-atrix-unmask | Reveal a subtree's text (never passwords). |
data-atrix-block | Replace a subtree with an empty box of the same size. |
| always | Canvas and iframes are not recorded. Requests are recorded as URL (secrets redacted), method, status and timing: never headers or bodies. |
<div data-atrix-block>…card form…</div>
<p data-atrix-mask>Shipping to 12 Example Street</p>Console errors (300 characters) and uncaught exceptions are recorded for the player's timeline; turn them off
with captureConsole: false, and request timings with captureNetwork: false.
iOS and Android (native and React Native)
| Control | Effect |
|---|---|
| default | All text and every input is masked. Masked text is never serialised: not its characters, not its length. |
| secure entry / password | Always masked, even inside an unmasked subtree. |
atrix-block | One opaque placeholder; nothing inside is walked. Use it for chat threads and payment sheets. |
atrix-mask / atrix-unmask | Mask or reveal a subtree. The nearest marker wins. |
| images | An average tone, never pixels. Maps, video, SVG, Skia and web views are placeholders. |
Where the marker goes:
- React Native:
<View {...maskProps("block")}>or<AtrixMask mode="block">, which setnativeID. Or listtestID/nativeIDvalues innativeReplay({ blockedIdentifiers, maskedIdentifiers }). - iOS:
view.atrixReplayPrivacy = .block, or anaccessibilityIdentifiercontainingatrix-block. Oroptions.blockedIdentifiers. - Android:
view.atrixReplayPrivacy, a Stringview.tagor a ComposetestTagcontainingatrix-block; orblockedTags/blockedViewClassesin the options.
Each native recorder ships with a test that records a replica of a real chat app (names, phone numbers, messages, a composer draft) through the real recorder, outbox and uploader, and fails if any of it appears on disk or in an upload.
Sampling, limits and retention
- Sampling is decided per session id with the same hash on the device and the server, so a session is either recorded completely or not at all.
- Length. Recording stops four hours after a session starts.
- Quota. Replays over your plan's allowance are sampled automatically rather than billed as a second meter. When a project reaches its spend cap, replay is the first thing dropped.
- Retention follows your plan's retention, rounded up to a storage tier, and is enforced by lifecycle rules on the object store.
- Consent. Nothing is recorded unless consent is
granted.deniedstops recording and deletes anything buffered on the device.