- core-web-vitals
- html
- performance
How images cause Cumulative Layout Shift, and how to reserve their space
Images without dimensions are a top cause of layout shift. See how CLS scores shifts, how width and height reserve space, and how to size art-directed images.
By Focal team 7 min read
Cumulative Layout Shift, or CLS, measures unexpected movement of visible content during the whole life of a page. Google rates a CLS of 0.1 or less as good and anything over 0.25 as poor, measured at the 75th percentile of page loads on mobile and desktop separately. web.dev’s guide to optimizing CLS puts images without dimensions first in its list of common causes. This post scores the shift from one unsized image, shows how browsers turn width and height into reserved space, and covers responsive and art-directed images.
How CLS scores a shift
A layout shift happens when a visible element changes its start position from one frame to the next. web.dev notes that an element that only changes size does not count, as long as it moves nothing else. An image that grows from zero height to full height does not shift. The text it pushes down does.
The browser scores each shift by multiplying two numbers:
- The impact fraction is the share of the viewport that the moving elements cover, before and after the move combined.
- The distance fraction is the farthest any element moved, divided by the viewport’s larger dimension.
CLS does not add up every shift on the page. It groups shifts into bursts that web.dev calls session windows. A window continues while each shift comes less than 1 second after the previous one, and it lasts 5 seconds at most. The page’s CLS is the score of its worst window.
Shifts within 500 milliseconds of a tap, click or key press carry the hadRecentInput flag and do not count. Scrolling is not recent input, so a shift during a scroll still counts.
One unsized image, worked through
The 2025 Web Almanac explains that the browser lays out an image without dimensions as if it had zero height. When the file arrives, the image takes its real height and the content below it moves down.
Take a phone viewport of 390 by 844 CSS pixels. An article shows a 3:2 photo at the full width of the screen, 120 pixels from the top. Text as wide as the screen fills everything below it. When the file arrives, the photo becomes 260 pixels tall, and every line of text moves down by 260 pixels.
text area, before and after combined: full width, 120px to 844px = 724px of height
impact fraction = 724 ÷ 844 = 0.858
distance fraction = 260 ÷ 844 = 0.308
layout shift score = 0.858 × 0.308 = 0.264
That single shift scores 0.264, which puts the page in the poor range on its own. Add width="1200" height="800" to the image and height: auto to its CSS, and the browser reserves the 260 pixels before the file arrives. The text never moves, and the score is 0.
Many pages still skip this step. The 2025 Web Almanac found that 62% of mobile pages and 65% of desktop pages have at least one image without dimensions. The median unsized image is about 100 pixels tall. The same chapter reports a good CLS on 81% of mobile pages and 72% of desktop pages.
How browsers turn width and height into a ratio
The HTML standard maps the width and height attributes of an <img> to a presentational hint of the form aspect-ratio: auto w / h. web.dev explains that browsers compute it as they process the HTML, before any image data arrives. Chrome shows the hint in the Elements panel:
img[Attributes Style] {
aspect-ratio: auto 640 / 360;
}
MDN’s compatibility data lists this behaviour in Chrome and Edge from version 79, Firefox from 71 and Safari from 15.
The standard also maps the attributes to the CSS width and height properties. A stylesheet that narrows the image but sets no height leaves the height at the attribute value. The box then has the wrong ratio, and the default object-fit value, fill, stretches the image to match it. Pair every width rule with height: auto:
img {
max-width: 100%;
height: auto;
}
The file has to match the attributes
The auto keyword in that hint matters. MDN’s page on aspect-ratio explains that for an <img>, the given ratio applies until the image loads. After that, the file’s own ratio takes over. If the attributes say 3:2 and the file is 4:3, the box changes height when the file arrives. web.dev notes that this still shifts the layout, though less than an image with no dimensions.
The attributes reserve the right space only when every file the page can load has the same ratio. For a srcset, web.dev recommends giving every candidate the same aspect ratio, so one pair of attributes fits them all. The guide to srcset and sizes covers how the browser picks a candidate.
CSS aspect-ratio for fixed-shape boxes
Some designs need a fixed shape whatever the photo, such as a grid of square product tiles. CSS aspect-ratio without the auto keyword keeps the box at that ratio after the image loads. MDN notes that it takes effect only when width or height is automatic, so reset the height here too.
.tile img {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
object-fit: cover;
}
The box never changes shape, but a file with a different ratio still has to fit inside it. Jake Archibald shows that without auto, the image stretches unless you set object-fit. MDN describes the options. The value cover fills the box and clips whatever falls outside. The value contain shows the whole image and letterboxes it. With cover, the browser still downloads the pixels it clips.
Art direction with picture
Art direction serves a different crop at different screen sizes, such as a square crop on phones and a wide one on desktops. One pair of attributes on the <img> cannot describe two ratios. Put width and height on each <source> instead.
<picture>
<source media="(max-width: 799px)" srcset="sofa-square-800.jpg"
width="800" height="800">
<img src="sofa-wide-1600.jpg" width="1600" height="900"
alt="Green velvet sofa in a bright living room">
</picture>
The selection steps in the HTML standard say that when the chosen <source> has these attributes, the <img> takes its dimensions from that <source>. Otherwise it uses its own. MDN’s compatibility data lists width and height on <source> in Chrome and Edge from version 90, Firefox from 108 and Safari from 15.
Lazy-loaded images shift after the load
web.dev’s CLS guide notes that shifts often happen while the user scrolls, when lazy-loaded content arrives without reserved space. Those shifts count, because a scroll is not recent input. MDN calls width and height especially important on lazy-loaded images.
Lab tools such as Lighthouse usually measure only the initial load. The Chrome UX Report measures CLS over the whole life of the page. A page can pass in the lab and still fail in the field. PageSpeed Insights shows both numbers. web.dev says a gap between them most likely comes from shifts after the load.
Do not lazy-load the main image near the top of the page. The guide to images and LCP explains why.
Serving the declared size with Focal
Focal takes the output size from the URL. Without a fit mode, Focal resizes and crops the image to fill the box exactly. A URL with 600x400 returns a 600 by 400 file whatever the shape of the upload. Write the same numbers into width and height, and the file matches the attributes. That still holds when an editor replaces the photo with one of a different shape.
The fit-in mode keeps the whole image inside the box, so one side can come back shorter than the box. Add the fill() filter to pad the file to the full box with a colour:
https://img.focaltool.com/your-key/unsafe/fit-in/600x600/filters:fill(white)/products/chair.jpg
A size such as 800x0 follows the source’s ratio instead, so your attributes depend on the source file. To read its dimensions, add the /meta/ segment. Focal then returns JSON with the source dimensions and encodes no image.
For art direction, request each crop from the same upload. Each URL returns a file with the exact size its <source> or <img> declares.
<picture>
<source media="(max-width: 799px)"
srcset="https://img.focaltool.com/your-key/unsafe/800x800/products/sofa.jpg"
width="800" height="800">
<img src="https://img.focaltool.com/your-key/unsafe/1600x900/products/sofa.jpg"
width="1600" height="900" alt="Green velvet sofa in a bright living room">
</picture>
The alignment segments choose which part of the photo survives each crop. The URL API reference lists them.
To check your own pages, record a trace in the Chrome DevTools Performance panel. The Layout shifts track groups shifts into clusters, and the Layout shift culprits insight lists possible causes. Compare the result with the field CLS in PageSpeed Insights, because a trace of the page load misses the shifts that come later.
Sources
- web.dev, Cumulative Layout Shift (CLS)
- web.dev, Optimize Cumulative Layout Shift
- HTTP Archive, Web Almanac 2025, Performance chapter
- HTML Standard, Rendering, attributes for embedded content and images
- HTML Standard, Images
- MDN, The img element
- MDN, aspect-ratio
- MDN, object-fit
- Jake Archibald, Avoiding img layout shifts, aspect-ratio vs width and height attributes
- MDN browser-compat-data, img element
- MDN browser-compat-data, source element