TulipLot works best in Chrome or a Chromium-based browser. Some features may be limited here.
← All posts
Advanced · · 9 min read

What six iframes actually cost your browser

Checked on 2026-08-24.

A dashboard with six panels looks like six tags in the HTML. Inside Chrome it's closer to six browsers. Each cross-site frame gets its own renderer process, with its own memory, its own main thread, and its own copy of the engine that draws it.

That's where writing about frame performance usually goes wrong: it quotes a number. What a frame costs depends on your machine, your Chrome build, and above all on what the framed sites load. Every figure below is one I measured on 2026-09-04, on one laptop — Intel Core i7-13700H, 32 GB of RAM, Windows 11 Pro build 26200, Chrome 152.0.7977.76, fresh profile, no extensions, no other tabs. Where I couldn't measure, you get the method.

One frame, one process

Chromium's Site Isolation documentation is blunt. The feature "ensures that pages from different websites are always put into different processes, each running in a sandbox that limits what the process is allowed to do," and it names the frame case: "Cross-site documents are always put into a different process, whether the navigation is in the current tab, a new tab, or an iframe."

Note the word site, which is wider than origin. Chromium defines it as "the scheme and registered domain name, including the public suffix, but ignoring subdomains, port, or path." Two frames on different subdomains, or on different ports, are one site, and they're allowed to share a process.

This is process isolation, and it isn't the isolation the neighboring articles describe. The sandbox attribute takes capabilities away from a frame; cookie partitioning keeps its login state apart from yours. Those are security boundaries you opt into. This one's a browser defense against Spectre-style attacks and compromised renderers, and you don't opt into it — it's on either way.

I built a page with six frames pointing at six loopback addresses — 127.0.0.2 through 127.0.0.7, six distinct sites, each serving one line of HTML. Chrome went from 8 processes on a blank page to 14, and from 3 renderer processes to 9. Six frames, six new renderers, one apiece.

Count carefully, though: frames get reused. Chromium's process model documentation says an iframe "will be placed in an existing example.com process (in any browsing context group), even if the process limit has not been reached," since "most iframes/fenced frames are less resource demanding than top-level documents." Two frames on the same loopback address added one renderer, not two.

Where the memory goes

Six near-empty cross-site frames took Chrome's total working set from 587 MB to 897 MB, and its private bytes from 320 MB to 441 MB. That's about 52 MB of working set and 20 MB of private memory per frame, for a one-paragraph document.

The two figures disagree because working set counts pages shared between processes: the engine's own code is mapped into every renderer, so adding it up six times counts it six times. Private bytes is closer to what each frame really adds.

Chrome's own accounting agrees with the smaller figure. In its task manager, the six subframe rows read 18,208K, 18,236K, 18,752K, 20,992K, 21,020K and 21,064K in the Memory footprint column, each with a different Process ID; the tab hosting them read 21,696K. Roughly 18 to 21 MB is the floor here — what a cross-site frame costs before it does anything.

Real sites cost more, and the multiplier is content, not framing. I reran the test against six sites that permit framing — of the 60 popular apps we measured, only 7 do. Chrome ended at 16 processes and 2.2 GB of working set, with its largest renderer holding 308 MB, and the shared GPU process alone grew from about 188 MB to about 607 MB. Two of those pages pulled in a reCAPTCHA frame from a third site, so six frames became eight — and both reCAPTCHA frames shared one process, as the reuse rule predicts. You don't control how many processes your six tags become.

Chromium publishes one figure for the platform-wide cost: "Higher overall memory use in Chrome. On desktop in Chrome 67, this is about 10-13% when isolating all sites with many tabs open." That's their measurement, of that build, in that scenario — not a per-frame number, and not mine.

What loading="lazy" actually defers

The loading attribute lives in the HTML Standard, and its lazy keyword gets one line: "Used to defer fetching a resource until some conditions are met." The default runs the other way — "The attribute's missing value default and invalid value default are both the Eager state" — so you have to ask.

The word that matters is defer. When an iframe is inserted the browser runs "Create a new child navigable for insertedNode" and only then processes the attributes; if the element will lazy load, processing stops before the navigation step. The element, its layout box and the nested navigable all exist, holding the initial about:blank document. What hasn't happened is the fetch, and therefore the process.

I measured that gap on the same six real sites. Six lazy frames parked below a 15-viewport spacer left Chrome at 8 processes and 3 renderers, indistinguishable from a blank page, with no frame targets at all. Scrolled to the bottom, the same page settled at 16 processes and 10 renderers, matching the eager run exactly. Lazy loading defers the bill. It doesn't discount it.

One specified bonus: an iframe "whose current navigation was lazy loaded boolean is false potentially delays the load event." Inverted, that says a lazy frame doesn't delay the parent's load event.

Now the part people state too confidently: there's no threshold distance to quote. The HTML Standard says "The lazy load scroll margin is an implementation-defined value," and lists suggestions rather than rules. MDN puts it the same way from the other side — lazy will "Defer loading of the iframe until it reaches a calculated distance from the visual viewport, as defined by the browser." A pixel count is one browser on one day. I used fifteen viewport heights so the result wouldn't depend on it.

One more limit isn't about performance: lazy loading applies only when scripting is enabled, which the spec calls "an anti-tracking measure."

Six main threads, one network process

Each renderer has its own main thread, so each frame runs its own timers and its own animation-frame callbacks, unaware of the others. I probed that with frames counting requestAnimationFrame callbacks and firings of a 10 ms setInterval, reported to the parent by postMessage. On a visible tab, the top document and the on-screen frame each ran 60.0 animation frames and 100.0 timer wakeups per second across 40 seconds. Neither slowed the other.

The network stack, though, isn't per frame — and that's the assumption I'd correct most often. Chromium's network service README describes a single utility process that "houses only the network service, so there is nothing running on its main thread," and notes that this configuration "is preferred for isolation and stability, and is the default on most platforms." My process tables agree: six extra frames, still exactly one network service.

What is split per frame is the cache: Chrome keys it on the top-level site and the frame's site together, so a font your page already holds gets fetched again inside a cross-site frame. Chrome published what that change cost: "The overall cache miss rate increases by about 3.6%, changes to the FCP (First Contentful Paint) are modest (~0.3%), and the overall fraction of bytes loaded from the network increases by around 4%." Those are their aggregate numbers from 2020.

The frame you can't see is nearly free

Scroll a cross-site frame off screen and something dramatic happens. In the same probe, with the tab fully visible, the off-screen frame ran zero animation frames in 40 seconds, and its 10 ms interval fired once per second instead of a hundred times — while its own document.visibilityState still said visible.

That's Chrome's choice, not a rule of the platform. The HTML Standard says a navigable has a rendering opportunity "accounting for hardware refresh rate constraints and user agent throttling for performance reasons, but considering content presentable even if it's outside the viewport," and adds that it "does not mandate any particular model for selecting rendering opportunities."

Hide the whole tab and it gets stricter. With my probe in a background tab, all three documents ran zero animation frames, and a 200 ms interval in the top document fired at a median gap of 1000 ms (minimum 983, maximum 1014). That matches Chrome's documented behavior for hidden pages: "The browser will check timers in this group once per second." After five minutes hidden, with a few more conditions met, Chrome's blog says those timers get checked "once per minute."

So a frame you can't see costs almost nothing to run. It still costs its process, which stays resident whether or not anything inside it runs — which is why frames are cheap to keep and expensive to have.

Measure it on your own machine

  1. Open the page you care about, frames and all.
  2. Open the task manager. Chromium documents the path as "Chrome Menu -> More tools -> Task manager (Shift+Esc)." In Chrome 152 it opens with three views — Tabs & extensions, Browser and All tasks — and the columns Task, Memory footprint, CPU, Network and Process ID.
  3. Find the row starting Tab: for your page, then count the rows below it starting Subframe:. Each names a site, without the port. Chromium's own check is this: "Verify that the main page and the subframe are listed in separate rows associated with different processes."
  4. Compare the Process ID values; right-click the column header to add that column when a build hides it. Distinct IDs mean distinct processes; if two frames share one, they're same-site.
  5. Add up Memory footprint across the tab row and its subframe rows, then subtract the same total for a blank tab.
  6. Switch to All tasks for the GPU Process and the Network Service; those are shared, so don't bill them to one page.
  7. Add loading="lazy" to the frames below the fold, reload, and don't scroll. The subframe rows shouldn't be there.

Run each variant twice. My repeats matched exactly on process counts and to within 10 MB on totals. The real-site run happened once.

Questions

Do two iframes of the same site get two processes?

No. Two frames on one loopback address added a single renderer, and that's what Chromium's aggressive-reuse rule describes for out-of-process iframes.

Is loading="lazy" worth adding to every frame?

To every frame starting below the fold, yes: it costs nothing and defers a whole process. Above the fold it does nothing, since the frame's already in view. It's a scheduling change, never a saving.

How many frames are too many?

There's no threshold I'd trust enough to print. My measured floor was about 20 MB per empty cross-site frame; the real pages I framed pushed a single renderer to 308 MB. Measure yours with the recipe above, on the sites you want and the machine you use.