Session Replay Basics 2026-07-27

How Much Does Session Replay Actually Slow Down Your Site?

Short answer: on a typical app, session replay costs you tens of kilobytes of gzipped JavaScript, a few percent of main-thread time, single-digit megabytes of memory, and a trickle of batched network uploads. That’s negligible. On an app that mutates thousands of DOM nodes per second — a virtualized data grid, a live trading dashboard, a collaborative editor — it is not negligible, and anyone who tells you otherwise hasn’t profiled one.

The question “how much does session replay slow down a site” doesn’t have one answer. It has four, because replay recorders spend four different resources, and they scale with different things. Let’s separate them, then I’ll show you how to get your own numbers in an afternoon.

The four costs, separated

Bundle size: fixed, and the least interesting

Most DOM-based recorders (rrweb and everything descended from it) land somewhere in the tens of kilobytes gzipped. That’s real weight, but it’s a one-time, cacheable cost, and every serious recorder loads asynchronously so it doesn’t block parse or first paint.

The trap here isn’t size. It’s when the script evaluates. A recorder that initializes synchronously during your app’s boot competes with hydration for the main thread. Defer it. If your vendor’s snippet doesn’t defer by default, that tells you something about their priorities.

CPU: scales with DOM churn, not traffic

This is the cost that matters. DOM recorders work by taking a full serialized snapshot of the document once, then subscribing to a MutationObserver and serializing every subsequent mutation into a diff. The observer callback runs on your main thread. Its cost is roughly proportional to the number and size of mutations per frame.

So the question isn’t “how fast is the recorder” — it’s “how much does your app mutate the DOM.” A marketing site or a CRUD form mutates almost nothing between interactions. Recorder cost: effectively zero. A data grid re-rendering 500 rows on every scroll tick generates a mutation storm, and the recorder has to serialize all of it, every frame, while your app is also trying to hit 60fps.

I profiled this pattern on a mid-range laptop with 6x CPU throttling in Chrome DevTools: on a form-heavy app, the recorder’s functions barely registered in the flame chart. On a virtualized table scrolling at full speed, serialization became one of the larger main-thread consumers during the scroll. Same recorder, same config. The variable was my app, not the SDK.

Two sub-costs hide inside CPU: input masking (string transforms on every keystroke — cheap) and mousemove capture (fires constantly; every sane recorder throttles it, and you should verify yours does).

Memory: the event buffer

Recorded events accumulate in an in-memory buffer between uploads. On normal sessions this stays in the single-digit-megabyte range. Long-lived tabs are where it bites: a dashboard someone leaves open for eight hours, mutating continuously, can grow a buffer you’ll notice in a heap snapshot. Recorders deal with this by flushing on an interval and periodically taking a fresh full snapshot so old diffs can be dropped. Check the flush interval. If it’s configurable and you have long-lived tabs, shorten it.

Network: batched, compressed, background

Uploads happen every few seconds, gzipped, usually via fetch with keepalive or sendBeacon on unload. Order of magnitude: an active session generates somewhere between a few KB and a few hundred KB per minute depending on DOM complexity. On desktop broadband this is noise. On a metered mobile connection it’s a real consideration — which is one argument for sampling mobile sessions at a lower rate than desktop.

How much does session replay slow down a site? Measure it yourself

Vendor benchmarks are run on vendor-chosen pages. Here’s the protocol I actually use; it takes about an afternoon.

  1. Pick your worst page. Not your homepage. The page with the most DOM churn — the grid, the editor, the live-updating dashboard.
  2. Record two DevTools performance profiles of the same scripted interaction (scroll the grid for ten seconds, type into the form), one with the recorder loaded and one without. Use 4x or 6x CPU throttling, because your users’ hardware is worse than yours.
  3. Compare main-thread time attributed to the recorder’s functions in the bottom-up view. Also compare long task counts — a recorder that adds total time but never exceeds a frame budget is much less harmful than one that adds 100ms tasks.
  4. Check the field, not just the lab. If you collect Core Web Vitals via the web-vitals library or your RUM tool, ship the recorder to 50% of sessions and compare INP distributions between the cohorts for a week. Lab profiles find mechanisms; field data tells you whether users can feel it.

I wrote up the general version of this A/B methodology — it applies to every third-party SDK you run, not just replay — in a separate piece on measuring SDK overhead.

One more thing worth checking while you’re in there: replay of the replay itself. Some tools compress or serialize on the main thread; better ones move packing into a web worker. Look at where the recorder’s time lands in the flame chart. Worker time is nearly free. Main-thread time during an interaction is the expensive kind.

Mitigations that actually move the needle

In descending order of impact:

Block the hot subtree. Every rrweb-lineage recorder supports a block class or selector that stops serialization for an element and its descendants. Put it on the data grid. This is the single biggest lever — it turns your pathological case back into the negligible case. The replay shows a placeholder box where the grid was, which is usually an acceptable trade: you still see what the user clicked, the errors, the network calls.

Sample. You almost never need 100% of sessions. Recording 20–50% of sessions (and 100% of sessions with errors, if your tool supports error-triggered capture) cuts aggregate cost proportionally and you lose approximately nothing for debugging purposes.

Leave canvas recording off. Canvas capture works by snapshotting the canvas contents on an interval, which is dramatically more expensive than DOM diffing. Unless the bug you’re chasing is inside the canvas, keep it disabled. Same logic for recording cross-origin iframes and fonts.

Throttle mousemove and scroll sampling. Usually on by default. Verify, don’t assume.

What doesn’t move the needle much: agonizing over the recorder’s bundle size delta between vendors. A 15KB difference in an async, cached script is not where your performance budget dies.

The honest conclusion

For most apps — forms, dashboards with modest update rates, content sites, checkout flows — a properly configured DOM recorder is below the noise floor of your field metrics. I’ve looked for the INP delta on apps like this and failed to find one that survived a week of data.

For DOM-churn-heavy apps, the overhead is real, measurable, and fixable with block rules and sampling. But “fixable” requires someone to actually run the profile. The failure mode isn’t session replay being slow. It’s teams installing it with defaults, never profiling their worst page, and either eating the cost silently or — worse — ripping out a genuinely useful debugging tool because of a performance problem a one-line block rule would have solved.

Measure your app. Not their demo.

See the bug the way your user did

LogReplay captures session replays, console output, network requests, and errors in one timeline — so you stop guessing what happened before the ticket arrived.

Try LogReplay free