Quick Tips: How to fix "Image elements do not have explicit width and height" in Page Speed Insights / Lighthouse
Tags: webdev, html, css, beginners
Published: 2021-06-06 18:49:32

If you have run your website through [Google Lighthouse](https://github.com/GoogleChrome/lighthouse), [Page Speed Insights](https://developers.google.com/speed/pagespeed/insights/), [web.dev measure](https://web.dev/measure/) etc. you may have seen an action point of "Image elements do not have explicit width and height". 

So how do we fix it?

## The short Answer
All you need to do is add the images *native* width and height to the image. 

You can do this with the `width` and `height` attributes, defining the sizes in pixels. 

This then allows the browser calculate an aspect ratio (the ratio of the width to the height) and allocate enough space for the image before it loads.

So for an image that is 1600 pixels wide by 900 pixels tall:

``` html
<img width="1600" height="900" src="your-image.jpg">
```

## Long Answer
Lets take a typical example.

You have the following in your CSS file:

``` css
img{
    width: 100%;
    height: auto;
}
```

This gives the browser enough information to calculate the images width as it can look at it's parent container width and calculate how wide the image should be.

`height: auto` however, is where the problem lies. It does not give the image an explicit height, but instead it is instructing the browser to make the image whatever height it thinks is appropriate based on the width of the image.

The problem with this is that the browser does not know the width or height of the original image when the HTML is parsed. It only finds out how large the image is when it starts downloading.

For this reason the following happens:
 - The browser downloads your HTML
 - it finds an image element and looks for any styles associated with it (which you should have [inlined as part of your critical CSS](https://www.smashingmagazine.com/2015/08/understanding-critical-css/))
 - it then renders the image element but does not know the height so gives it a 0 pixel height
 - it then requests the image and finds out it is 1600 pixels by 900 pixels
 - it then uses that information to create an aspect ratio of 16:9
 - it then revisits the image element it found earlier, looks at the calculated width (let's say it is 400 pixels on the screen) and applies the aspect ratio to calculate the height (400 / 16 * 9 = 300px).

This results in what is known as a Layout Shift. This is then added to the [Cumulative Layout Shift (CLS)](https://web.dev/cls/) on the site.

As this is part of [Google's web vitals](https://web.dev/learn-web-vitals/) and is very soon to be a metric that affects your search rankings, it is something you want to fix **now**.

## How to fix it
There are a few ways to fix this, but here are two of the recommended ways to fix it, with the first being the preferred / best practice.

### Option 1 - define the width and height of the image using the `width` and `height` attributes.

The first option is to define the width and height of the image using the `height` and `width` attributes.

``` html
<img width="1600" height="900" src="some-image.webp">
```

Notice how the attributes do not include the units (pixels), they should be defined as integers.

In modern browsers this will then be used to calculate an aspect ratio for the image.

If you remember back to the previous section where we described the order of events in a browser, calculating the aspect ratio happened after the image element had been rendered as we didn't know the height of the image yet.

By applying a `width` and `height` attribute the browser can now calculate the aspect ratio **before** it renders the image element and can reserve enough space for the image once it downloads.

[Using the `width` and `height` attributes is the recommended best practice to avoid layout shifts from images.](https://web.dev/optimize-cls/?utm_source=lighthouse&utm_medium=devtools#modern-best-practice)

**Top tip:** One thing to note is that the browser will still obey and CSS sizing you apply. 

So if you apply `width:100%` and `height:auto` as in the original CSS example it will still work correctly and obey your CSS rules. The only difference is that it can now calculate how big `height: auto` needs to be.

**Bonus tip:** Another interesting thing is that your height and width attributes only need to be the correct aspect ratio. So all of the following would result in the same behaviour as the ratio between the width and the height is the same:
 - `<img width=1600 height=900>`
 - `<img width=800 height=450>`
 - `<img width=160  height=90 >`

It is worth noting though that using as close as possible to the correct size is recommended just in case something goes wrong with your CSS.

## Option 2 - "aspect ratio boxes"
This technique is also known as "the CSS padding hack".

What we do here is instead of defining the width and height on the image, we define the height of the image as a percentage of the width within our CSS file.

In order to achieve this we give our image a height of zero pixels (which is how it would render initially remember) and then use `padding-top` to allocate the correct amount of space in the document to avoid a layout shift.

### HTML
```html
<div class="image-div"></div>
```

### CSS
``` css
.image-div {
  overflow: hidden; /* not necessary if div is empty and other styles do not interfere */
  height: 0; /* not necessary if div is empty and other styles do not interfere */
  padding-top: 56.25%; /*aspect ratio of 16:9 at 100% width requires a 56.25% height*/
  background: url(/my-image.webp);
} 
```

Or, if you don't need to [support Internet Explorer you can use `calc()`](https://caniuse.com/calc)

### CSS using `calc()`
``` css
.image-div {
  overflow: hidden; /* not necessary if div is empty and other styles do not interfere */
  height: 0; /* not necessary if div is empty and other styles do not interfere */
  padding-top: calc(900 / 1600 * 100%); /*aspect ratio of 16:9, notice how we reverse the values as we are calculating the height from the width this time*/
  background: url(/my-image.webp);
} 
```

[This padding technique is explained in more detail in this article from CSS tricks](https://css-tricks.com/aspect-ratio-boxes/)

#### Why would you use this?
Obviously it is a bit of a hack and it requires you to use a background image on a div.

It is also not useful for anything other than decorative images as you cannot define an `alt` attribute for people who use assistive tech or have images switched off.

But it does have the advantage of keeping your HTML clean if you are a purist...but I don't think that is a good enough reason to use it!

## What if you don't know the exact dimensions of the image?
Both the above techniques have one big flaw that you may have spotted.

When designing the page you have to know the width and height of the image to calculate the aspect ratio / add the appropriate width and height.

Obviously the solution is to handle this on the back end and use caching techniques to store the width and height attributes but this may not be possible.

If you are trying to fix this issue on an existing site that has thousands of images, or you cannot calculate the image width and height etc. you may have to make a minor compromise.

### The fixed height container
Lets assume that all images on your site lie somewhere between a 1:1 aspect ratio and a 16:9 aspect ratio.

What we could do is add a container around all of our images that has a fixed height.

That way a browser is able to allocate space for the container on the page instead of the image. 

The downside is you will end up with white space at the bottom of that container for images that are not the same aspect ratio.

The following fiddle demonstrates this technique:

{% jsfiddle https://jsfiddle.net/2dc0hjan/ result html css %}

I added `background-color: #ccc` to the container to show where the extra white space would be. 

This technique is probably best used when images on a site are *similar* aspect ratios (some are 16:9, some are 16:10 for example) as the extra white space will not be as noticeable.

## Conclusion
To wrap up, Cumulative Layout Shift needs to be minimised for a great user experience and soon to maintain your Search Engine Rankings.

The 3 techniques described above should hopefully let you fix this issue on your site if you haven't already done so.

I would recommend finding a way to automate adding a `width` and `height` attribute to your images for 99% of scenarios.

The only time this won't work is if you serve images with a different aspect ratio depending on the screen size (known as art direction) using the `srcset` attribute. In that scenario you will have to either use media queries and the padding hack or the fixed height container technique. 

## For the Algo!
My new sign off experiment! 

If you enjoyed this article, give it a ❤, if you thought it was special give it a 🦄 and above all, don't forget:

---

> Leave a comment for the algorithm! Especially if you have a scenario where you think the above techniques won't work 😁!