Fixing CLS from Video and Iframe Embeds
Embedded videos, maps and social media posts are some of the most stubborn sources of layout shift. The page loads with an empty space, a default-sized frame or a plain link, and then a script or the player itself resizes the embed and shoves the rest of the page down.
Unlike an ordinary image, an iframe does not tell the browser its shape, so the usual image fixes are not enough. This guide explains why each type of embed moves, how to spot which one is doing it, and how to lock its box so it can load whenever it likes without moving anything.
Why embeds move the page
An <iframe> is a window onto another page. The browser has no way to know, before
that page loads, what shape its content will be, so an iframe has no natural aspect ratio. Its width
and height attributes set a fixed size in pixels, but unlike on an image they are not turned
into a ratio the browser can scale.
That leads to a typical chain of events on a responsive site:
- The embed code from YouTube or Vimeo says
width="560" height="315". On a phone that is too wide, so the theme addsiframe { width: 100%; height: auto; }. - With the height set to auto and no ratio to work from, the iframe falls back to the browser default height of 150 pixels.
- To fix the squashed player, a script such as an old "fit videos" plugin runs after load, reads the attributes and sets the correct height. The iframe grows by a hundred pixels or more and everything below it moves.
Other embeds fail in different ways. A native <video> element with no dimensions
starts at a default size and then resizes when its metadata reveals the real video shape. A social media
post is first a plain blockquote with a link, then the platform's script swaps it for an iframe whose
height is only known when the post has rendered. A map iframe may be given its height by a script that
waits for the map library.
Spotting a moving embed
Run the CLS Debugger on a page with the embed. The elements it reports will usually be the content under the embed, not the embed itself, so look at what sits directly above them. Then reproduce it:
- In Chrome DevTools, set slow network throttling and disable the cache. Record a reload in the Performance panel and select each layout shift to see what moved.
- Select the iframe or video in the Elements panel and look at the Computed tab. A height of exactly 150 pixels on an iframe, or 150 on a video element with a width of 300, means the browser had no size to use.
- Check the inline
styleattribute on the iframe or its wrapper after load. A height added there that is not in the page source was set by a script. - Block the embed's domain in the Network panel, for example
youtube.comor the social platform's script, and reload. If the shift disappears, the embed is the cause; if the space collapses instead of staying reserved, you have not reserved any.
Scroll the whole page during the recording. Embeds are often lazy-loaded further down, and their shifts only happen when the visitor gets close.
The fix, step by step
Step 1
Identify which embed is resizing
Run the CLS Debugger and note the elements that move and what sits above them. Reproduce the load in Chrome with slow throttling, the cache disabled and a full scroll of the page. Inspect each iframe and video element for a computed height of 150 pixels or a height written into its style attribute by a script. Block the embed's domain and reload to confirm that the shift belongs to that embed.
Step 2
Wrap video iframes in a fixed-ratio box
Give each YouTube, Vimeo or similar iframe the CSS width: 100% and aspect-ratio: 16 / 9,
with height: auto, or put it in a wrapper with that ratio and make the iframe fill the wrapper.
Use 9 / 16 for vertical videos such as Shorts and 4 / 3 for older footage.
Keep the width and height attributes too, so the player has a sensible size if the stylesheet fails.
Step 3
Size native video before its metadata loads
For self-hosted <video> elements, add width and height
attributes matching the video's pixel size and set aspect-ratio and height: auto
in CSS, so the box does not start at the default 300 by 150 and then resize. Add a poster
image with the same proportions, and use preload="none" or metadata for videos
further down the page to save bandwidth for visitors who never play them.
Step 4
Reserve a realistic height for social posts
Social media embeds start as a blockquote and are replaced by an iframe of unpredictable
height. Wrap each one in a container with a min-height close to the typical finished size,
which you can measure by loading a few posts at phone and desktop widths. Choose a value at or slightly
above the usual height, since a small gap under a short post is far less disruptive than a jump.
Step 5
Remove scripts that resize embeds after load
Search the theme and plugins for older responsive video scripts, often called something like fitvids or responsive embeds, and for any code that measures iframes and writes a height. Once the CSS aspect ratio is in place they are no longer needed, and leaving them in can still change the height after load. For maps, set the iframe or map container's height in CSS rather than waiting for the map library.
Step 6
Swap heavy players for a same-size facade
For videos that most visitors never play, show a static thumbnail with a play button in
a box of exactly the player's ratio, and load the real iframe only when it is clicked. Because the facade
and the player share one box, the swap does not move anything, and the page avoids downloading the player's
scripts. Add loading="lazy" to iframes that stay as normal embeds below the fold.
Step 7
Retest with consent refused and accepted
Clear page and CDN caches and re-run the CLS Debugger. Then load the page in a private window twice, once refusing and once accepting cookies, because many consent tools replace blocked embeds with a notice of a different size. Make sure the notice fills the same fixed-ratio box as the player, and check phone and desktop widths while scrolling to every embed.
Embed markup that holds its shape
YouTube or Vimeo iframe
<div class="embed embed-16x9">
<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
width="560" height="315"
title="How we restore oak furniture at Hollin Workshop"
loading="lazy"
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
.embed { position: relative; width: 100%; }
.embed-16x9 { aspect-ratio: 16 / 9; }
.embed-9x16 { aspect-ratio: 9 / 16; max-width: 360px; margin-inline: auto; }
.embed iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
You will still see the older "padding-top: 56.25%" technique in embed code and themes. It does the
same job and works, but aspect-ratio is clearer and easier to change per video.
Self-hosted video
<video controls preload="metadata"
width="1280" height="720"
poster="/media/hollin-tour-poster.jpg">
<source src="/media/hollin-tour.mp4" type="video/mp4">
</video>
video { width: 100%; height: auto; aspect-ratio: 16 / 9; }
Setting the ratio in CSS as well as the attributes removes any doubt about how the browser treats the attributes on a video element, and the poster fills the reserved box straight away.
Google Maps and other fixed-height iframes
.map-embed iframe { width: 100%; height: 360px; border: 0; }
@media (min-width: 60em) { .map-embed iframe { height: 450px; } }
Maps have no natural shape, so a fixed height per breakpoint is simpler than a ratio. Whatever you pick, set it in the stylesheet so it applies before any script runs.
Embeds on common platforms
- WordPress. The block editor's embed block has a "resize for smaller devices" option that adds a fixed-ratio wrapper class. It relies on the theme supporting responsive embeds; themes that do not, or page builders with their own video widgets, may fall back to scripts that resize after load. Details for plugins and builders are in CLS in WordPress.
- Shopify. Product media videos are handled by the theme's media gallery. Videos pasted into rich text or added by apps often are not wrapped. Check the section or snippet that outputs them.
- Page builders and site builders. Many add their own lazy-loading and resizing for video widgets. Look for a setting that sets the aspect ratio in CSS, and turn off any "auto height" option.
- Hand-written sites. Add a single
.embedclass like the one above to your stylesheet and use it for every iframe you paste in.
Background videos on hero sections are a special case: they fill a container that already has its size from CSS, so they rarely shift the layout. If one does, the container is the problem, not the video. For images in the same hero area, see fixing CLS from images without dimensions.
Keeping embeds stable
- Provide one embed component. Whether it is a block, a shortcode, a snippet or a React component, have one way to embed a video that always applies the fixed-ratio wrapper, and ask editors to use it instead of pasting raw iframe code.
- Clean up pasted iframes on output. A small filter in your CMS that wraps any iframe from a known video host in the embed class catches content pasted by people who skipped the component.
- Record the ratio for vertical video. As more sites embed short vertical clips, store the orientation with the video so the right wrapper is chosen.
- Retest when the consent tool changes. A new consent management setup can change how blocked embeds are displayed, and the placeholder must match the player's box.
For how consent tools themselves can move the page, see fixing CLS from dynamic content insertion.
1. Why do width and height attributes stop layout shift on images but not on iframes?
For images, browsers turn the two attributes into an aspect ratio that CSS can scale, so a responsive image with height auto still gets the right height before it loads. Iframes do not get that treatment, because the browser cannot know what shape the framed page will be. If your CSS sets an iframe's height to auto, it falls back to a default of 150 pixels. You need to give the iframe, or a wrapper around it, an explicit CSS aspect ratio.
2. What aspect ratio should I use for a YouTube embed?
Use 16 by 9 for standard YouTube videos, which is the shape almost all uploads use. For YouTube Shorts and other vertical clips, use 9 by 16 and limit the width so a tall player does not fill an entire desktop screen. Older footage may be 4 by 3. If the video's shape does not match the box, the player adds black bars inside the frame rather than resizing, so the page still does not shift.
3. Does lazy loading an iframe cause layout shift?
Not if the iframe's box already has a size. The loading lazy attribute only delays fetching the embedded page until the visitor scrolls near it. If the iframe, or its wrapper, has a fixed aspect ratio or height set in CSS, it takes up the right amount of space from the start and the content simply appears inside it. If the size is only set once the embed loads, lazy loading moves the shift to the moment the visitor scrolls near it.
4. What is a video facade and does it help CLS?
A facade is a lightweight stand-in for an embedded player, usually the video's thumbnail with a play button drawn over it. The real player iframe is only loaded when the visitor clicks. It helps CLS as long as the facade occupies exactly the same fixed-ratio box as the player, so swapping one for the other moves nothing. Its bigger benefit is speed, because the page no longer downloads the player's scripts for every visitor, most of whom never press play.
5. How can I stop Instagram or X posts from shifting my page?
You cannot know the final height of a social post in advance, so you either reserve a typical height or change how the post is shown. Wrap each embed in a container with a minimum height close to the usual finished size at each screen width, or use a fixed height with scrolling inside. Alternatively, show a screenshot or a text card linking to the post. Placing embeds lower on the page also reduces how much a remaining shift costs.
6. My cookie consent tool blocks embeds until consent is given. Can that cause shifts?
Yes. Many consent tools replace a blocked YouTube or map iframe with a notice asking for consent, and that notice is often a different size from the player. When consent is given, the notice is swapped for the real embed, and the page moves. Make the placeholder fill the same fixed-ratio wrapper as the player, so the swap happens inside a box that never changes size. Test both with consent refused and accepted.
7. Is the padding-top percentage trick still needed for responsive video?
No. The padding-top technique, where a wrapper has zero height and a padding of 56.25 per cent to create a 16 by 9 box, was the standard method before browsers supported the CSS aspect-ratio property. It still works, and you do not need to rewrite it if it is already in place and stable. For new work, aspect-ratio is simpler to read, easier to change per video, and works on the iframe or video itself without extra wrapper markup.
The CLS Debugger records every layout shift while your page loads, names the elements involved, calculates the cumulative score and ranks each shift by its impact, so you can tell whether a video, map or social post is to blame.
Open CLS Debugger Fixes โ
Social media posts and other unknown heights
Posts from X, Instagram, Facebook, TikTok and similar platforms are the hardest embeds to stabilise, because their final height depends on the length of the text, whether there is an image or video, and the width of your column. The platform's script decides it after rendering the post.
overflow: auto.Whichever you choose, place social embeds away from the top of the page where possible. A shift halfway down a long article affects fewer visitors, and less of the viewport, than one just below the headline.
display: noneuntil it has loaded and then reveal it. Revealing it is exactly the kind of late insertion that counts as a shift. If you want a fade-in, useopacityon an element that already takes up its space.