Image layout shift happens when a picture loads and pushes the content around it down or sideways because the browser did not know how much room to leave. The fix is to tell the browser the image's shape before it downloads, by adding width and height attributes, by using the CSS aspect-ratio property, or both. Once the space is reserved, the picture fills it and nothing moves.
This guide is for site owners and developers who see a high Cumulative Layout Shift (CLS) score, or who notice text jumping as a page loads. You will learn how browsers turn width and height into a ratio, how to handle responsive, lazy-loaded and background images, iframes and fonts, and how to find shifts with Chrome DevTools and Lighthouse.
Before you begin
You will need:
- Access to your templates, theme or CMS, so you can edit image markup and CSS.
- Google Chrome with DevTools (F12, or Ctrl+Shift+I on Windows and Linux, Cmd+Option+I on a Mac).
- The addresses of the pages that feel jumpy. Start with templates that carry many images, such as article, product and category pages.
Back up templates before editing and change one template at a time.
What CLS is and the 0.1 threshold
A layout shift is an unexpected movement of visible content between one frame and the next. Cumulative Layout Shift (CLS) is the Core Web Vitals measure of how much of that movement visitors experience. Each shift gets a score based on how much of the screen moved and how far. CLS reports the worst burst of shifts, grouped into windows of up to five seconds, so one bad moment counts more than many tiny ones. Movement that follows a click or tap, within about half a second, is excluded because the visitor caused it.
The "good" threshold is a CLS of 0.1 or lower, measured at the 75th percentile of page visits. That means at least three quarters of real visits should score 0.1 or less. Above 0.25 is rated poor. See Core Web Vitals Explained: LCP, INP and CLS Thresholds for the other two metrics.
Why image layout shift happens when space is not reserved
When a browser reads HTML, it lays out the page before images arrive. If an <img> has no size information, the browser reserves no height for it, so the image takes up almost no space. When the file finishes downloading, the browser learns the real size, makes room, and every element below it moves down. The later the image arrives, the more content has already been drawn, and the larger the visible jump.
On a slow phone connection the same page can jump several times.
How width and height attributes reserve space
Add the image's intrinsic pixel size as plain numbers, with no units:
<img src="/img/team-1200.jpg" width="1200" height="800" alt="Our team at the launch event">
Modern browsers read those two numbers before the file loads and work out an aspect ratio, here 3:2. They then reserve a box with that shape. When your CSS sets only a width, for instance width: 100%, the browser calculates the matching height from the ratio. This is why the attributes still help on responsive pages even when the image is displayed at a different size from the numbers.
The numbers only describe the shape. They do not force the size, provided your CSS lets the height adjust. The standard rule is:
img {
max-width: 100%;
height: auto;
}
max-width: 100% stops an image from overflowing its container. height: auto lets the height follow the width using the ratio. Without height: auto, a CSS width combined with a fixed height attribute can stretch or squash the picture. Check current browser support for attribute-based ratios at caniuse.com, although support is long established in current major browsers.
The CSS aspect-ratio property
The aspect-ratio property sets the preferred shape of a box in CSS:
.hero-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
Use it when a layout needs a different shape from the file, for example square thumbnails cut from varied photos. object-fit: cover fills the box and crops the overflow. object-fit: contain shows the whole image inside the box.
You can also write aspect-ratio: auto 16 / 9. The browser uses the image's own ratio once it knows it and falls back to 16:9 before that. MDN documents this form; check caniuse.com for current support in the browsers you target. The property also works on containers, such as a <div> that will hold an embed, a video or a background image.
| Approach | Best for |
|---|---|
width and height attributes |
Every <img> in content. Needs height: auto in CSS. |
CSS aspect-ratio |
Cards, thumbnails, containers and embeds. |
| Both together | Responsive cards: attributes give a default, CSS sets the final shape. |
Responsive images with srcset
When you offer several widths of the same image with srcset, give every candidate the same intrinsic shape and put the width and height of the default file on the <img>:
<img
src="/img/guide-800.jpg"
srcset="/img/guide-400.jpg 400w, /img/guide-800.jpg 800w, /img/guide-1600.jpg 1600w"
sizes="(min-width: 60em) 50vw, 100vw"
width="800" height="450"
alt="Checklist on a laptop screen">
Every file here is 16:9, so the reserved box matches whichever one the browser picks. If candidates have different shapes, the box can change when the real file arrives, and that causes a shift. Different crops for different screens, sometimes called art direction, are done with a <picture> element. Support for width and height on <source> is newer than on <img>, so check caniuse.com; the alternative is a CSS aspect-ratio rule per breakpoint. For choosing widths and sizes, see Responsive Images with srcset and sizes: Serve the Right Size.
Lazy-loaded images
Setting loading="lazy" delays the download until the image is near the viewport. Without reserved space, the box is empty until the file loads, and the content shifts when it does. A shift is typically only counted when it moves something visible, but it still disturbs readers who scroll. With width and height or aspect-ratio in place, a lazy image fits its reserved space with no movement.
Do not lazy load the first large image on a page, since that can delay Largest Contentful Paint. See Lazy Loading Images for SEO: Defer Images Without Hiding Them.
Background images, iframes and embeds
CSS background images have no intrinsic size in the layout. A container whose only content is a background image collapses to zero height unless you size it. Give it an aspect-ratio or a fixed min-height:
.banner {
aspect-ratio: 3 / 1;
background: #e8eef5 url("/img/banner.jpg") center / cover no-repeat;
}
The background-color shows while the image loads.
Iframes and embeds such as maps, video players and social posts are replaced elements without a built-in ratio, so a browser typically gives an iframe a default size of 300 by 150 pixels until you set another. Set both attributes, or wrap the embed:
<iframe src="https://example.com/embed/123" width="560" height="315" title="Product demo video"
style="width:100%; height:auto; aspect-ratio:16/9; border:0"></iframe>
Content injected later, such as ads or cookie banners, shifts pages the same way, so reserve a slot with min-height or aspect-ratio. For deferring iframes safely, see Lazy Load Iframes and Video Embeds Without Hurting the Page.
Web fonts as another cause
Images are not the only source. When a web font loads after the page has drawn text in a fallback font, the text can change size and wrap differently, moving everything below it. Choosing font-display: swap shows text sooner but allows that swap. Reduce the jump by picking a fallback font with similar proportions, using size-adjust and related @font-face descriptors where your target browsers support them, and preloading the one or two fonts used above the fold. The details are in Font Display Swap: Load Web Fonts Without Layout Shift.
Code examples
A responsive image
<img class="content-img" src="/img/report-1200.webp" width="1200" height="675"
srcset="/img/report-600.webp 600w, /img/report-1200.webp 1200w"
sizes="(min-width: 48em) 720px, 100vw"
alt="Line chart of monthly site speed checks">
.content-img { display: block; max-width: 100%; height: auto; }
A card grid
<ul class="cards">
<li class="card">
<a href="/guides/a"><img src="/img/a-640.jpg" width="640" height="360" loading="lazy" alt="Launch checklist"></a>
<h3>Launch checklist</h3>
</li>
</ul>
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 1rem; list-style: none; padding: 0; }
.card img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
Every card image has the same box, so a row does not change height as individual files arrive.
An image inside a link
<a class="brand" href="/"><img src="/img/logo.svg" width="160" height="40" alt="Example Co home"></a>
.brand { display: inline-block; }
.brand img { display: block; width: 160px; height: auto; }
An image is inline by default, so it sits on the text baseline and a small gap can appear below it inside a link. Making the image display: block removes the gap, and the width and height stop the link collapsing before the image loads.
Step-by-step instructions
Step 1: Measure the shift
Run Lighthouse from the DevTools Lighthouse tab, or test the page at pagespeed.web.dev. Note the CLS value and the elements Lighthouse lists as contributing to shifts. Audit names change between versions, so look for diagnostics about layout shifts or images without dimensions.
What success looks like: you have a CLS value and a list of elements to check.
Step 2: See the shifts happen
Open DevTools, press Ctrl+Shift+P (Cmd+Shift+P on a Mac), type "Rendering" and choose Show Rendering. Tick Layout Shift Regions, then reload. Areas that move are briefly highlighted. Next, record a reload in the Performance panel. The shifts appear in an experience or layout shifts track, and selecting one shows its score and the elements that moved. Panel layouts change between Chrome versions, so use the names as a guide.
What success looks like: you can name the specific images, embeds or text blocks that move.
Step 3: Add dimensions to every image
For each image in your list, add width and height equal to the file's intrinsic size. In a CMS, check whether your theme prints them automatically, since many do. Add img { max-width: 100%; height: auto; } to your stylesheet if it is missing.
What success looks like: each <img> in the page source has both attributes.
Step 4: Reserve space for everything else
Add aspect-ratio or min-height to containers for background images, iframes, ads and banners. Make sure all srcset candidates share one shape.
What success looks like: with the network throttled, blank areas keep their shape until content arrives.
Step 5: Check fonts
Throttle the network and watch the first screen. If text jumps when the web font arrives, apply the font techniques above.
Step 6: Retest in the lab, then watch field data
Repeat Steps 1 and 2. Lab results change as soon as you deploy. Field data from real visits, such as the Core Web Vitals report in Search Console, is typically collected over about 28 days, so it improves gradually. A default Lighthouse run observes the page load, while field CLS can include movement later in the visit, such as after scrolling. A good lab score is therefore not proof of a good field score. See Field Data vs Lab Data: Why Lighthouse and Real Users Disagree.
What success looks like: the lab CLS drops, and the shifting elements no longer appear in the Performance panel.
Using Crawvix Image Doctor to find images without dimensions
Crawvix's Image Doctor lists up to 2,000 images written into the HTML of the pages of a full scan of a verified website and checks up to 200 of them for status, type and file size. It reports missing width and height among other findings, such as missing or generic alt text, broken images, large files, a lazily loaded first image, older formats and generic file names. Each finding comes with evidence and what to do, and there is a CSV export. It only reads your website and never changes it. CSS backgrounds and embeds are better checked by hand in DevTools.
Final verification checklist
- Lighthouse and the Performance panel show no remaining shifts from images.
- Every
<img>haswidthandheightset to the intrinsic size. - Your CSS includes
max-width: 100%andheight: autofor content images. - All
srcsetcandidates share one aspect ratio. - Lazy-loaded images have reserved space.
- Background-image containers, iframes and embeds have an
aspect-ratioormin-height. - Web font loading does not cause visible text jumps.
- Field CLS is rechecked after several weeks.
Common mistakes to avoid
- Setting
heightin CSS to a fixed value. It fights the attribute-based ratio and can distort the picture. - Forgetting
height: auto. A CSS width plus a fixed attribute height squashes the image. - Mixing shapes in
srcset. Each candidate should have the same ratio. - Relying on lab results alone. Check real-user data too.
Frequently asked questions
Do width and height attributes make images non-responsive?
No. In current browsers they supply an aspect ratio, and your CSS still controls the displayed size. With max-width: 100% and height: auto, the image scales down on small screens and keeps its shape.
What is a good CLS score?
A CLS of 0.1 or lower at the 75th percentile of page visits is rated good, and above 0.25 is rated poor, according to Google's Core Web Vitals guidance.
Should I use the aspect-ratio property or width and height?
Use width and height on every content image, because they work in the HTML before CSS loads. Add CSS aspect-ratio when a layout needs a particular shape or the container has no image inside it.
How do I find which image is causing layout shift?
Tick Layout Shift Regions in the DevTools Rendering tab and reload, or record the load in the Performance panel and select the layout shift entries. Lighthouse also lists elements that contributed to shifts.
Why is my CLS different in Lighthouse and Search Console?
Lighthouse is a lab test of one load on a simulated device. Search Console reports field data from real visits, which can include shifts later in the visit and many different devices.
Related guides
- Reduce Cumulative Layout Shift (CLS) Step by Step
- Responsive Images with srcset and sizes: Serve the Right Size
- Lazy Loading Images for SEO: Defer Images Without Hiding Them
- Lazy Load Iframes and Video Embeds Without Hurting the Page
- Font Display Swap: Load Web Fonts Without Layout Shift
- Core Web Vitals Explained: LCP, INP and CLS Thresholds
- Field Data vs Lab Data: Why Lighthouse and Real Users Disagree
- Improve Lighthouse Performance Without Breaking Your Website
Official references
- https://web.dev/articles/cls
- https://web.dev/articles/optimize-cls
- https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img
- https://developer.chrome.com/docs/devtools/performance
- https://web.dev/articles/layout-instability-api
Next step: Check your images with Image Doctor or see how Crawvix checks a website. This guide gives technical information and no guarantee of rankings, traffic or index inclusion.
Crawvix Editorial Team
· Published November 1, 2026 · Last updated November 1, 2026 · 11 min read