How to Lazy-Load iframes
This guide extends Lazy-Loading Images Without Hurting LCP, part of Image & Media Optimization. An iframe is a whole document: a YouTube embed loads its own HTML, several hundred kilobytes of JavaScript, fonts and a poster image; a map embed loads tiles and scripts; a social post loads a widget runtime. Each one competes with your own page for network and CPU, and much of that work happens on the main thread of the same process for same-site or non-isolated frames.
Most embeds sit below the fold, and many are never scrolled to. Deferring them is one of the highest-return fixes on content pages. Browsers support loading="lazy" on iframes natively, and a facade — a static image that swaps in the real iframe on click — goes further by avoiding the embed entirely until the user asks for it.
Rapid Diagnosis
- List iframes with
document.querySelectorAll('iframe')and note which are below the fold. - Group requests by domain in the Network panel; youtube.com, googlevideo, maps.googleapis and social widgets are typical heavy hitters.
- Check main-thread activity. In the Performance panel, look for long tasks attributed to embed scripts during load.
- Check whether the embed is ever used. Analytics on play or interaction rates show how much of the cost is wasted.
Root Cause Analysis
1. Iframes load eagerly by default. Without an attribute, every iframe in the HTML starts loading immediately.
2. Embeds are heavy. A single video embed can exceed 1MB of transfer before anyone presses play.
3. Third-party scripts run early. Embed runtimes parse and execute JavaScript during the critical load window.
4. Low interaction rates. Most visitors never play the video or open the map, so the cost buys nothing for them.
Step-by-Step Resolution
1. Add loading="lazy" to below-the-fold iframes
<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
loading="lazy" width="560" height="315" title="Product demo video"
allow="accelerometer; encrypted-media; picture-in-picture" allowfullscreen></iframe>
<!-- trade-off: the browser still loads the full embed once it is near the
viewport, even if the user never plays it; facades avoid that cost. -->
Always include width and height (or an aspect-ratio on a wrapper) to reserve space and prevent layout shifts.
2. Replace video embeds with a facade
<button class="yt-facade" data-id="VIDEO_ID" aria-label="Play: Product demo video"
style="aspect-ratio:16/9;background:#000 url(https://i.ytimg.com/vi/VIDEO_ID/hqdefault.jpg) center/cover">
<span class="play-icon" aria-hidden="true"></span>
</button>
document.addEventListener('click', (e) => {
const btn = e.target.closest('.yt-facade');
if (!btn) return;
const f = document.createElement('iframe');
f.src = `https://www.youtube-nocookie.com/embed/${btn.dataset.id}?autoplay=1`;
f.allow = 'autoplay; encrypted-media; picture-in-picture';
f.allowFullscreen = true; f.title = btn.getAttribute('aria-label');
f.style.cssText = 'width:100%;aspect-ratio:16/9;border:0';
btn.replaceWith(f);
});
// trade-off: the user waits for the embed to load after clicking (often under a
// second); warm the connection with preconnect on hover to shorten it.
3. Warm connections on intent
On pointerenter or focus of a facade, inject <link rel="preconnect"> for the embed's domains, so the click-to-play delay is shorter.
4. Use static images for maps
A static map image (from the provider's static API or a self-generated screenshot) linking to the interactive map is often enough. Load the interactive embed only on click.
Verification
Reload with the cache cleared and confirm no requests to the embed domain occur before scrolling (lazy) or before clicking (facade). Check that the iframe area keeps its size so CLS does not change. In the Performance panel, total blocking time during load should drop. Confirm that keyboard users can focus the facade and activate it with Enter or Space.
Worked Example: A Recipe Blog
A recipe site embedded a YouTube video and an Instagram post in every recipe, both well below the ingredients list. Initial transfer was 3.1MB, TBT on a mid-range phone was 980ms, and INP p75 was 340ms because the embed scripts ran during the user's first interactions. Video play rate was 6%. The team swapped the video for a facade and the Instagram post for a static image linking to the post. Initial transfer fell to 640KB, TBT to 210ms, and INP p75 to 170ms. Video plays did not decrease — they rose slightly, possibly because the poster image loaded faster than the embed had.
Choosing Between Lazy and Facade
Use native loading="lazy" when the embed is likely to be used by most visitors who reach it, or when instant interactivity matters (a booking widget, a payment frame, an interactive calculator). Use a facade when the embed is optional and heavy, and interaction rates are low — videos, maps and social posts are the classic cases. For iframes that are part of the core page above the fold, such as a checkout payment frame, load them eagerly and focus on keeping the embed itself lean, since deferring them would delay the main task.
Auditing Embeds Across a Site
Embeds accumulate through content editors rather than code reviews, so they are easy to miss. Crawl a sample of templates and count iframes per page with their domains, or log iframe elements found on load from RUM with a one-line script. Rank embed types by total transfer multiplied by page views, and handle the top few with facades or static images. Then add a guard: a content component (a video or map shortcode) that renders the facade by default, so editors cannot paste raw embed code into articles. This keeps pages fast after the cleanup, rather than drifting back one pasted embed at a time.
Common Mistakes
- Lazy-loading an above-the-fold iframe. Delays content the user sees first.
- Facades without accessible labels. Screen reader users cannot tell what the button does.
- No reserved space. The iframe pops in and shifts the layout.
- Using a full-size poster image. A facade with a 300KB poster loses part of its benefit; use a sized thumbnail.
Edge Cases
Autoplay requirements. After a click, browsers usually allow autoplay with sound; mobile browsers may still require a second tap in some cases.
Privacy and consent. Facades also help consent flows — no third-party requests happen until the user acts.
Hidden iframes. Iframes with display: none or zero size may be loaded eagerly regardless of the attribute; avoid relying on lazy loading for hidden tracking frames.
Print and reader modes. Facades show the poster; include a text link to the video for those contexts.
FAQ
Do all browsers support loading="lazy" on iframes?
Current versions of the major browsers support it. Unsupported browsers simply load the iframe eagerly, so it is safe to add.
Is there a ready-made YouTube facade?
Yes — lightweight web components such as lite-youtube-embed implement the facade pattern with accessibility and preconnect warming built in.
Does a facade hurt SEO for videos?
Search engines can understand videos from structured data (VideoObject) and links. Add structured data if video search visibility matters.
How far ahead does native lazy loading start?
Browsers use a distance threshold that depends on connection speed, typically a few viewport heights. You cannot configure it.
Does lazy loading affect INP?
Yes, positively. Embed scripts running during load often cause long tasks that delay the first interactions. Deferring them frees the main thread.
Should I sandbox embeds?
The sandbox attribute restricts what an iframe can do, which is good for security, but many embeds need specific permissions to work. Test carefully and grant only what is needed.
Related
- Native lazy loading vs IntersectionObserver — the same choice for images.
- Lazy-loading CSS background images — the other element type without native lazy loading.
- Replacing YouTube embeds with a facade — a deeper look at video facades.