/ โ† CLS Debugger Fixes / CLS from Images
In this guide
  1. Why an unsized image moves the page
  2. Proving it is an image and not something else
  3. The fix, step by step
  4. Before and after markup
  5. Awkward cases and how to handle them
  6. Where the tags come from on common platforms
  7. Keeping new images sized
  8. Questions and answers

Why an unsized image moves the page

A browser lays out a page before most of its files have arrived. When it reaches an <img> tag, it needs a width and a height to know how much room to leave. If the tag says nothing about size and no CSS rule fixes the height, the browser has to guess, and its guess is zero pixels tall. The paragraph after the image is placed directly under the heading above it.

A moment later the image data downloads. The browser now reads the picture's real pixel size from the file header, works out how tall it should be at the current width, and re-lays out the page. The paragraph that was sitting just under the heading is now several hundred pixels lower. That jump is a layout shift, and the CLS Debugger will report the paragraph, and everything after it, as the elements that moved.

How the attributes fix it

Modern browsers use the width and height attributes on an image in a clever way. Their default stylesheet turns the two numbers into an aspect ratio, roughly equivalent to aspect-ratio: auto 1200 / 800. So even when your CSS makes the image fluid with width: 100%, the browser can calculate the right height for whatever width the column happens to be, before a single byte of the file has arrived. The box is reserved, the text is placed underneath it, and nothing moves when the picture fills in.

This is why the attributes do not have to match the displayed size. They describe the shape of the image. A 1600 by 900 photo shown at 400 pixels wide is fine with width="1600" height="900", as long as your CSS lets the height follow the width.

๐Ÿ’ก The word "auto" in that default ratio means that once the real file loads, its own proportions win. If you give the wrong numbers, the box is reserved at the wrong shape and then corrects itself, which is a smaller shift but still a shift. Use the file's true dimensions.

Proving it is an image and not something else

A paragraph moving down does not always mean an image above it was unsized. Fonts, ad slots and banners produce the same symptom. Before editing templates, make sure the mover and the cause line up.

  1. Run the CLS Debugger on the page and note the largest shifts and the elements it names. Look at what sits directly above each named element in the page.
  2. In Chrome DevTools, open the Network panel, set throttling to a slow profile and tick Disable cache. Reload and watch the page. An image problem looks distinctive: text appears, then a picture pops in and the text drops by roughly the height of the picture.
  3. Open the Elements panel, select the image and look at its tag. If there are no width and height attributes, check the Computed tab for aspect-ratio and height. If the computed aspect ratio is auto with no numbers and the height is auto, the browser had nothing to reserve.
  4. As a quick proof, block the image in the Network panel (right-click the request and choose block request URL) and reload. If the page no longer jumps at that point, the image was the cause.

You can also list every image on a loaded page that is missing size information by pasting this into the console:

[...document.images].forEach((img) => {
  const cs = getComputedStyle(img);
  const sized = img.getAttribute('width') && img.getAttribute('height');
  if (!sized && cs.aspectRatio === 'auto') {
    console.log(img.currentSrc || img.src, img);
  }
});

Anything it prints is an image the browser could not size before download, unless a fixed CSS height is doing the job instead.

The fix, step by step

Step 1

Confirm the shift comes from an image

Run the CLS Debugger and write down the top shifts, the elements named and roughly when they happen. Reproduce the load in Chrome with slow network throttling and the cache disabled, and watch for text dropping at the moment a picture appears. Block that image request and reload: if the jump disappears, you have found the cause. If it does not, look at fonts, ads or inserted notices instead of editing images.

Step 2

List every unsized image on the template

Paste the console snippet from this guide into DevTools on each main template, such as the home page, a blog post, a product page and a category page. Note which images come from the theme, which come from editors writing content, and which are added by plugins or apps. Fixing the source that generates the tag, rather than individual pages, is what stops the problem from coming back next week.

Step 3

Add the intrinsic width and height

Give each <img> tag width and height attributes equal to the real pixel size of the file, without units, for example width="1200" height="800". In templates, read the numbers from the media library or image metadata rather than typing them. For srcset images, use the dimensions of any candidate with the same proportions. For inline SVG icons, set the attributes or a viewBox plus a CSS size.

Step 4

Let CSS scale the image without losing the ratio

Add a rule such as img { max-width: 100%; height: auto; } so the width follows the column and the height is calculated from the attributes. Search the stylesheet for rules that set a fixed height on images without also fixing the width, because they stretch the picture. Where a design needs a crop, set both dimensions or an aspect-ratio and add object-fit: cover so the image fills the box without distortion.

Step 5

Reserve space for images you cannot tag

Some pictures have no tag you control: CSS backgrounds on hero sections, images injected by a gallery script, or pictures whose size is unknown until an API responds. Give their wrapper an explicit aspect-ratio, such as aspect-ratio: 16 / 9, or a fixed min-height matching the finished layout. The box then keeps its size while empty, and the image fills it without pushing anything else down the page.

Step 6

Keep lazy-loaded images in sized boxes

Lazy loading only delays the download; it does not reserve space, so every lazy image still needs its attributes or a sized wrapper. Replace older JavaScript lazy loaders that swap a one-pixel placeholder for the real file with native loading="lazy". Do not lazy-load the main image near the top of the page, because it delays your largest content and gives the box less time to be filled before the visitor starts reading.

Step 7

Re-test slowly and on a narrow screen

Clear any page cache and CDN cache, then re-run the CLS Debugger on each template you changed. Reproduce the load again with throttling at a phone width and at a desktop width, scrolling all the way down so lazy images below the fold load as well. Check that no picture looks stretched or squashed. Then watch real-user CLS over the following weeks as older visits drop out of the report.

Before and after markup

Here is the pattern that causes the problem, followed by the corrected version. The business, file names and sizes are fictional.

<!-- Before: no size information, so the box starts at zero height -->
<img src="/media/greenbank-bakery-shopfront.jpg" alt="Greenbank Bakery shopfront">

<!-- After: intrinsic size in attributes, fluid in CSS -->
<img src="/media/greenbank-bakery-shopfront.jpg"
     width="1600" height="1067"
     alt="Greenbank Bakery shopfront">
/* In the stylesheet */
img {
  max-width: 100%;
  height: auto;
}

/* A cropped card image that must always be square */
.card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

Background images in CSS

A hero section that uses background-image has no intrinsic size at all, because the element's height comes from its content, not from the picture. That usually causes no shift on its own. The problem appears when a script measures the image and then sets the section's height, or when the section is empty until the image loads. Set the size up front:

.hero {
  background: #e8e2d8 url("/media/hero-kitchen.jpg") center / cover no-repeat;
  aspect-ratio: 21 / 9;
  min-height: 280px;
}

The background colour gives a calm placeholder while the file downloads, and the box never changes size.

Images inside flex and grid layouts

Inside a flex row, an image with height: auto and attributes behaves well, but a rule like align-items: stretch on the parent, or min-width: 0 missing on a flex child, can make the reserved ratio be ignored until load. If a card grid still jumps after adding attributes, inspect the card and check whether the image wrapper has its own height set by the flex or grid algorithm. Giving the wrapper an explicit aspect-ratio is the most reliable fix.

Awkward cases and how to handle them

SituationWhy it still shiftsWhat to do
Editor-uploaded images in articlesThe rich text editor or a Markdown converter writes a bare tag with only src and alt.Add dimensions when the content is rendered, reading them from the stored file. Most CMSs can do this in an output filter.
SVG logos and iconsAn SVG with no width, height or viewBox has no ratio the browser can use.Add a viewBox inside the file and size the <img> or inline SVG in HTML or CSS.
Images from a third-party feedReview widgets, Instagram grids and product feeds insert pictures of unknown size.Fix the size of the container, crop with object-fit, and request thumbnails of a fixed size where the feed allows it.
Wrong numbers in the attributesA theme hard-codes 800 by 600 while the uploaded file is 1200 by 1600.Generate the attributes from the real file. A fixed crop on the server also guarantees the shape.
Placeholder swapped for the real imageAn old lazy loader shows a 1 by 1 GIF, then swaps in the real picture with a different ratio.Use native lazy loading, or keep attributes on the tag so the ratio is right from the start.
Image hidden until loadedA fade-in script sets display: none until the load event.Use opacity for the fade so the box keeps its space.
โš ๏ธ Do not fix the shift by setting a fixed pixel height on every image, such as img { height: 300px; }. It removes the jump but squashes or stretches pictures at every other screen width. Let the height follow the width and the ratio.

If a picture swaps between files with different proportions at different screen sizes, for example a wide banner on desktop and a tall one on phones, that is a different problem with a different fix, covered in fixing CLS from responsive image swap.

Where the tags come from on common platforms

The fix is the same everywhere, but where you apply it depends on how your site generates image tags.

๐Ÿ’ก When you are in the template anyway, check the alt text and file size too. Adding dimensions does not make a large image load faster; it only stops it moving things around.

Keeping new images sized

Image shifts tend to come back quietly, because new content is added every week by people who are not thinking about layout. A few habits keep the problem closed:

For how image dimensions fit with compression, formats and lazy loading more generally, see the image optimisation fixes.

Questions and answers

1. Do width and height attributes make my images fixed size and stop them being responsive?

No, as long as your CSS sets the height to auto. The attributes tell the browser the proportions of the image before it downloads. A rule such as max-width 100 per cent with height auto then lets the image shrink or grow with the column while keeping that shape. The numbers are only used as a starting ratio and as a fallback size if no CSS applies, so a 1600 by 900 image can still display at 350 pixels wide on a phone.

2. What numbers should I put in the width and height attributes?

Use the actual pixel dimensions of the image file, or any pair of numbers with exactly the same proportions. If the file is 2400 by 1600, then 2400 and 1600 or 1200 and 800 both give the same ratio. What matters is the shape. Numbers that do not match the file make the browser reserve the wrong box, which then corrects itself when the image arrives, so the page still shifts, only by a smaller amount than before.

3. Is the CSS aspect-ratio property better than width and height attributes?

They do the same job for layout, and you can use either. Attributes are best for ordinary content images because they travel with the tag and describe the file itself. The aspect-ratio property is better when the shape belongs to the design, such as every product card being square, or when there is no img tag at all, such as a CSS background or an empty container that a script fills later. Many sites use attributes on images and aspect-ratio on wrappers.

4. Does native lazy loading cause layout shift?

Lazy loading does not cause shift by itself, but it makes an existing sizing problem more visible. Because a lazy image loads as the visitor scrolls towards it, an unsized one expands while they are reading nearby text, and that movement counts towards CLS because scrolling does not exclude shifts. If every lazy image has width and height attributes or sits in a sized box, it can load at any time without moving anything.

5. Why does my page still shift after I added width and height to every image?

The most common reasons are a CSS rule that overrides the height, such as a fixed height or height set by a script, attributes with the wrong numbers, a lazy loader that swaps in a placeholder of a different shape, or a cause that is not an image at all. Re-run the CLS Debugger and check which elements now move and when. Web fonts, cookie notices and ads often produce a very similar downward jump.

6. Do small icons and logos need width and height too?

Yes. A small icon at the top of the page can still shift the header or a line of text when it loads, and a logo in the site header shifts everything below it on every page. SVG files are a frequent culprit because many are exported without a viewBox or size. Give each icon and logo explicit dimensions in HTML or CSS, and check the header in particular, since it affects every page.

7. Will fixing image shifts improve my rankings?

It may help a little, but it is not a large ranking factor on its own. Core Web Vitals are part of Google's page experience signals, and a page with good CLS meets one of the three thresholds. Content relevance matters far more. The clearer benefit is to visitors: text that stays put is easier to read, and buttons that do not move get fewer mistaken taps, which tends to help enquiries and sales.

Find the images that move your page

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 see which images to size first.

Open CLS Debugger Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All CLS Debugger Fixes  ·  CLS from Responsive Image Swap  ·  CLS from Video and Iframe Embeds  ·  CLS Debugger Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.