Session replay
Reconstruct what a user actually did — recording control, privacy masking, quality and capture modes, and what replay costs.
Session replay reconstructs a session from captured frames and breadcrumbs, so you can see the path to a crash or a drop-off instead of inferring it.
What it captures#
Frames (periodic screenshots), touches, scrolls, navigation, network activity and log breadcrumbs — stitched onto one timeline alongside the session's events and errors.
Control recording#
Replay starts automatically when the feature is enabled and you are entitled to it. Control it explicitly when you need to:
await ScaleBun.replay.start();await ScaleBun.replay.pause(); // keeps the session, stops capturingawait ScaleBun.replay.resume();await ScaleBun.replay.stop(); // ends the recording sessionpause() and stop() are different in an important way: pause keeps the recording
session open so the timeline stays continuous, while stop ends it. Use pause around
a sensitive screen; use stop when the user opts out.
Check state before acting on it:
ScaleBun.replay.isRecording; // booleanScaleBun.replay.sessionId; // string | nullPrivacy#
Replay captures the screen, so treat masking as a requirement rather than a refinement.
await ScaleBun.replay.setPrivacy({ maskTextInputs: true, // strongly recommended for any form maskImages: true, // user-generated or account imagery redactAuth: true, redactCookies: true, redactBodies: false,});The same settings can be supplied at init, which is better — it means the very first frame is already masked:
await ScaleBun.init({ projectId: '…', publishableKey: '…', apiBaseUrl: '…', privacy: { maskTextInputs: true, maskImages: true },});Quality and capture mode#
await ScaleBun.replay.setQuality('low'); // smaller payloadsawait ScaleBun.replay.setCaptureMode('manual'); // you decide when frames happenawait ScaleBun.replay.captureFrame(); // one frame, on demandLower quality reduces upload size and battery cost, at the price of legibility on dense screens. Manual mode suits apps where only a few flows are worth recording.
Screens and breadcrumbs#
Screen names make a replay readable. If you set the navigation ref (Quick start), they arrive automatically. Otherwise:
await ScaleBun.replay.setScreen('Checkout');Add your own markers for context the SDK cannot infer:
await ScaleBun.replay.addBreadcrumb({ category: 'payment', message: 'Applied promo code', level: 'info',});Identity#
Replay keeps its own identity, so it can be disabled independently of analytics. Setting one does not set the other:
await ScaleBun.replay.setUser({ id: 'user_123' });await ScaleBun.replay.clearUser();Turning it off#
Three levels, from most to least permanent:
| Approach | Effect |
|---|---|
features: { replay: false } at init | Lane never starts; no capture, no traffic |
await ScaleBun.replay.setEnabled(false) | Off at runtime, can be re-enabled |
await ScaleBun.replay.pause() | Temporary, session stays continuous |
Not being entitled to replay on your plan has the same effect as the first: the collector is never registered, so it generates no traffic at all.
Cost#
Replay is the most expensive capability in the SDK — it uploads image data. Budget
for it deliberately: use low quality by default, consider manual capture for
high-traffic screens, and pause on screens whose content has no diagnostic value.
Related#
Full replay namespace reference — all 16 members.
Configuration —
sessionReplay,privacy,features.Troubleshooting — replay produces no frames.