The quick download:
Most websites use far more JavaScript and DOM memory than developers realize, yet few teams measure it.
-
At the median, top sites use about 10MB of JavaScript and DOM memory on desktop and 9.6MB on mobile, and the long tail climbs much higher.
-
Memory tracks closely with two metrics you already watch: the amount of JavaScript you ship and Total Blocking Time.
-
First-party frames account for roughly 84% of that memory, so most of the weight is yours to fix.
-
Build your own browser memory baseline, measure repeated user journeys, and alert on regressions instead of treating one-off numbers as hard limits.
Memory problems rarely show up in a page-load test. They usually appear after someone has been using an application for a while. A single-page application becomes sluggish, a mobile browser discards a tab because memory is running low, or a memory leak only appears after users repeat the same workflow several times. Traditional performance metrics often miss these issues because they don’t show you how much memory the page is actually consuming.
Measuring browser memory has also been difficult. Without reliable data, it’s hard to know whether a page is using an unusual amount of memory or whether that increase is affecting the user experience. Benchmarking helps answer that question by showing how your pages compare with others. To understand whether memory usage is affecting real users, though, Real User Monitoring can help. Correlating memory usage with user behavior, releases, and other performance metrics makes it much easier to identify regressions and decide what to investigate next.
The measureUserAgentSpecificMemory API
Chrome introduced an API for collecting memory-related information, called measureUserAgentSpecificMemory. It is an asynchronous method on the Performance interface that returns a Promise. The API is defined in the Measure Memory API specification, a WICG Community Group draft rather than a finalized W3C standard. Support is Chromium-only. Safari has shown no forward momentum on adoption, and Mozilla was still fine-tuning details in the proposed specification. Because results are implementation-dependent, you should not compare them across browsers.
The measureUserAgentSpecificMemory API returns an implementation-dependent estimate of the memory allocated by the web application, and it can include frames and workers. The breakdown categories it reports are determined by the implementation. In the 2021 setup, Chrome attributed memory to JavaScript and DOM elements.
According to research from the V8 team, 35% of memory allocation on the web is JavaScript related, and 10% is for representing DOM elements in memory. The remaining 55% is images, browser features, and everything else that ends up in memory. So while the categories observed here cover JavaScript and DOM information, that accounts for a large portion (45%) of the actual memory usage of a page.
An article by Ulan, who spearheaded much of the work for the API, provides a sample return object.
{
bytes: 60_000_000,
breakdown: [
{
bytes: 40_000_000,
attribution: [
{
url: "https://foo.com",
scope: "Window",
},
],
types: ["JS"]
},
{
bytes: 0,
attribution: [],
types: []
},
{
bytes: 20_000_000,
attribution: [
{
url: "https://foo.com/iframe",
container: {
id: "iframe-id-attribute",
src: "redirect.html?target=iframe.html",
},
},
],
types: ["JS"]
},
]
}
The response provides a top-level bytes property, which contains the total memory usage the implementation attributed to the page, and a breakdown array that shows where that memory allocation comes from.
With this information, we can see the total memory usage the implementation reported for a page, and we can also see how much of that memory comes from first-party frames versus third-party frames, and how much is globally shared.
That’s useful information, so the study ran a set of tests to see whether it could establish benchmarks around memory usage.
Setting up the tests
The measureUserAgentSpecificMemory API is promise-based, and using it only requires a few lines of code.
if (performance.measureUserAgentSpecificMemory) {
let result;
try {
result = await performance.measureUserAgentSpecificMemory();
} catch (error) {
if (error instanceof DOMException &&
error.name === "SecurityError") {
console.log("The context is not secure.");
} else {
throw error;
}
}
console.log(result);
}
Using it in production requires care because of the security considerations involved. For production use, the API relies on cross-origin isolation through COOP and COEP response headers. In a test lab, we can work around that differently.
First, the study passed a pair of test-lab-only Chrome flags to bypass the security restrictions: the aptly named –disable-web-security flag, along with the –no-site-isolation flag to accurately catch cross-origin memory usage. These flags are for controlled testing only and should never be used for real browsing. We can pass those through WebPageTest, so all that remains is a custom metric to return the measureUserAgentSpecificMemory breakdown.
[memoryBreakdown]
return new Promise((resolve) => {
performance.measureUserAgentSpecificMemory().then((value) => {
resolve(JSON.stringify(value));
});
});That snippet sets up a promise to wait for measureUserAgentSpecificMemory to return a result, grabs the full result, converts it to a string, and returns it so we can dig in. Measurement is tied to garbage collection, so the API may wait an implementation-specific interval before it resolves. In the 2021 setup, the custom metric used a 20 second timeout.
To come up with some benchmarks, the study set up the test with that metric and the Chrome flags, then ran it on the top 10,000 URLs (based on the Chrome User Experience Report’s popularity rank) on Chrome for desktop and again on an emulated Moto G4. Some tests didn’t complete because sites were down or blocking the test agents, so the study ended up with 9,548 test results for mobile and 9,533 desktop results.
Let’s dig in.
How much JS and DOM memory does the web use?
Let’s start by looking at memory usage of the top 10k URLs by percentile.

This chart reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.
These numbers came in larger than expected.
At the median, sites use ~10MB for JavaScript and DOM related memory on desktop URLs, and ~9.6MB for mobile. By the 75th percentile, that jumps to ~19.4MB on desktop and 18.6MB on mobile. The long-tail data from there climbs much higher.
To put this in context, remember that Chrome research puts JavaScript and DOM related memory at ~45% of memory usage on the web. Using that, the original 2021 study extrapolated a rough estimate of ~22.2MB of total memory for a single page on desktop and ~21.4MB for mobile at the median, and a rougher estimate of ~43.1MB of total memory for a single page on desktop and ~41.3MB for mobile at the 75th percentile. That’s a large amount of memory for a single page within a single window of a single application, on a machine that has to juggle the memory constraints of many simultaneously running applications and processes.
Without context around the business impact, it’s hard to say definitively how much is too much here.
It’s also unclear how much memory is available for JS related work in the first place. The legacy API (performance.memory) provides a jsHeapSizeLimit value that is supposedly the maximum size of the JS heap available to the renderer (not just a single page), but those values are proprietary and poorly specified, so we can’t rely on that to find our upper bound.
The study still used its results as rough benchmarks, similar to what has been done for other metrics where we lack good field data to judge impact. Borrowing the good/needs improvement/bad levels that Google has popularized around Core Web Vitals produced the following experimental bands, derived from the 2021 percentiles rather than from validated thresholds tied to user or business impact.
This table reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs. Treat the bands as an experimental gauge, not a limit. Let’s dig deeper and get more context about memory usage.
Correlation between memory and other performance metrics
As we noted earlier, measuring memory in the wild is tough to pull off right now. A lot of security mechanisms need to be in place to collect the data accurately, which means not all sites can get meaningful data today. That’s a real challenge, because performance data becomes more interesting when we can put it in the context of its impact on the business and overall user experience.
In lieu of that, the study looked at how memory usage correlated to other performance metrics, to see if any of them could serve as a reasonable proxy. Given the focus on JavaScript and DOM related memory only, the correlation between any paint related metrics or traditional load metrics was weak. Memory does, however, correlate strongly with the amount of JavaScript you send and with Total Blocking Time. (The closer the correlation coefficient is to 1, the stronger the correlation.)

This chart reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.
The takeaway is intuitive: if you’re passing a lot of JavaScript, expect to use a lot of memory as a result. And if your Total Blocking Time is high, it’s reasonable to expect that you’re consuming a high amount of JavaScript related memory.
Where does it all come from?
Next, let’s take a closer look at what that memory is comprised of.
Each entry in the breakdown array carries an attribution list and a types list, and we can read those to determine whether memory is related to first-party content, cross-origin content, or a shared or global memory pool.
Any cross-origin memory has a scope of cross-origin-aggregated, because cross-origin information may be aggregated for privacy. Any memory allocation with no scope designated is shared or global memory. Any bytes that have no scope other than cross-origin-aggregated are first-party memory usage.
It’s worth stressing that attribution is based on frame (iframes and similar). An attribution is not necessarily the URL of the JavaScript resource responsible for the memory, so first-party memory usage here could still be affected by third-party resources.
If we break down byte usage by where it’s attributed, we see that 83.9% of that memory is attributed to first-party frames, 8.2% to cross-origin frames, and 7.9% is shared or global for desktop.
Mobile is similar, with 84.6% of memory attributed to first-party frames, 7.5% to cross-origin frames, and 7.9% shared or global.

These pie charts reflect the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.
Framework-associated memory in the 2021 dataset
The study also looked at memory usage when popular frameworks are in use. The big caveat: this doesn’t mean all that memory is for the framework itself. There are many more variables in play. These tables describe sites that were detected as using a framework, which is observational, not controlled measurements of framework overhead. You should not use them to choose a framework.
Frameworks are worth examining because many maintain their own virtual DOM. The virtual DOM is an in-memory representation of an interface that frameworks use to sync up with the real DOM and handle changes.
So we’d expect memory usage to be higher when a framework that uses this concept is in place. That’s exactly what the data shows.

This table reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.

This chart reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.
There’s a risk of looking at that table and immediately deciding that using React, for example, is terrible for memory, while ignoring the other things we know. Memory usage is highly correlated to the amount of JavaScript on a page, and sites that use frameworks ship more JavaScript. So the sharp increase in memory usage could owe more to the fact that React sites tend to ship a lot of code than to any inefficiency in the framework.
Zeroing in on the actual memory efficiency of a framework is tougher because there’s a fair amount of noise involved. One gauge that could be interesting as a rough indication of memory efficiency is the ratio of bytes in memory compared to JavaScript bytes shipped. DOM elements do factor into memory use, but given the weak correlation between DOM elements and memory usage and the high correlation between JavaScript bytes and memory usage, this gives us a rough but useful benchmark. A lower ratio indicates better overall efficiency (fewer memory bytes per JavaScript byte shipped).

This table reflects the 2021 dataset: emulated Moto G4 for mobile, Chrome for desktop, and the top 10k CrUX URLs.
These results are surprising. React appears to have a lower memory to JS bytes ratio than the other comparison points. React also tends to result in a lot more JavaScript being shipped than the others (other than Angular), which is part of why we see such high memory usage even in the best case. Whether you’re using a framework or not, keep the total amount of JavaScript as small as possible.
There’s another caveat: this data is memory usage based on the initial page load. That’s interesting in itself, but it doesn’t tell us anything about potential memory leaks. In a few one-off tests, memory leaks were common in single-page applications, though that’s a topic for another post.
Building a browser memory baseline
Memory is still a largely unexplored area of web performance, and that probably needs to change. As we ship ever-increasing amounts of JavaScript, memory usage climbs alongside it.
Three questions remain open, and each one is a separate thing to reason about:
- How much memory does a page use?
- Is that amount abnormal for comparable pages?
- Does it harm users or business outcomes?
The 2021 benchmark answered the first two questions for the pages it tested and established a percentile-based reference point. The third question depends on connecting memory data to real user outcomes, and teams can start building their own baseline today.
A practical workflow looks like this:
- Measure repeated user journeys, not just the initial page load, so leaks that appear after several navigations show up in the data.
- Segment results by browser, device class, page type, and session length, because a median hides the long-tail cases that hurt users most.
- Set a baseline within the same browser and test configuration, since results are implementation-dependent and don’t compare across browsers.
- Alert on regressions against that baseline instead of treating the 2021 percentiles as universal limits.
- Correlate changes with JavaScript payload, Total Blocking Time, specific releases, and user outcomes so you can distinguish an isolated measurement from a meaningful regression.
Connecting browser memory with Real User Monitoring
Where teams can collect browser-memory telemetry, correlating it with real-user experience, synthetic tests, releases, and application or infrastructure signals can help distinguish an isolated measurement from a user-impacting regression. Connecting real user experience with application, infrastructure, and Internet-path evidence is how teams move from raw numbers toward business impact.
Connect browser memory data to real user experience.
LogicMonitor bring browser performance, synthetic tests, application, and Internet-path evidence into one view, so you can separate an isolated measurement from a regression that hurts users. See it working against your own pages.
FAQs
What is the measureUserAgentSpecificMemory API?
It’s an asynchronous method on the browser’s Performance interface that returns an estimate of the memory a web application has allocated, including frames and workers. Support is currently limited to Chromium-based browsers, which break the memory down across JavaScript and DOM elements. Because results are implementation-dependent, you should not compare them across browsers.
How much memory do JavaScript and the DOM actually use on a typical page?
In the 2021 benchmark of the top 10,000 URLs, the median page used about 10MB for JavaScript and DOM memory on desktop and 9.6MB on mobile. At the 75th percentile that rose to roughly 19.4MB on desktop and 18.6MB on mobile. Since JavaScript and DOM represent about 45% of total page memory, the full figure per page runs higher still.
Does using a framework mean higher memory usage?
Sites built with frameworks tended to use more memory, mostly because they ship more JavaScript. Memory correlates strongly with the amount of JavaScript on a page, so the framework itself may not be the main driver. The data is observational, so you should not use it to choose a framework.




