How set width and height attributes for <img> element within <picture> element to prevent layout shifts?

Viewed 1705

Running Lighthouse in Chrome DevTools it says CLS (Cumulative Layout Shift) needs improvement.

I’m aware of that modern browsers now set the default aspect ratio of images based on an image's width and height attributes. Hence best practice is that width and height attributes should be set to prevent layout shifts.

Despite this I have omitted setting width and height attributes since this is the only way I can get it to work the way I want (please see my code below).

Because I’m having images in WebP format (with png fallbacks) I’m forced to use the <picture> element (so that the browsers not supporting WebP get the png version of the image). Please note I’m using differently-sized versions of the same image.

Hence if I didn't have used WebP format I would go along with <img> solely and not <picture>. Problem would then have been solved thanks to the aspect-ratio.

As you can see in the code snippet below there are two versions of the same image in this particular example. Setting width="245" and height="600" in <img> result in that both my two differently-sized versions of the same image get the maximum size of 245 x 600px (it will of course scale down in accordance with smaller viewports). For wider viewports (min-width: 813px) according to my media query I'm serving the larger version of the same image but setting width and height in <img> the image scales down to 245 x 600px.

Now to my question/s; If possible, what can I do to be able to set the width and height attributes for my <img> element within my <picture> element to prevent layout shifts (and also get other benefits for doing so such as faster load times)? Hence, how do I get the browsers to make use of the aspect ratio to show my images the correct way?

I would really appreciate someone’s guidance with this.

Thanks / Carl

<picture>
            <source 
              media="(max-width: 812px)" 
              srcset="image-245w.webp"
              type="image/webp"
            >
            <source 
              media="(min-width: 813px)" 
              srcset="image-332w.webp"
              type="image/webp"
            >
            <source 
              media="(max-width: 812px)" 
              srcset="image-245w.png"
              type="image/png"
            >
            <source 
              media="(min-width: 813px)" 
              srcset="image-332w.png"
              type="image/png"
            >
            <img class="img-fluid" src="image-245w.png" alt="image">     
</picture>
2 Answers

I have done some testing and I came to the conclusion that using width and height (as you should) on the image element cancels out the sizes attribute. You can't get your responsive image to work properly anymore if you set the width and height.

I have created a small repo that shows different combinations: https://www.gijsvandam.nl/responsive-picture-element/

The solution to this is to not use sizes if you use width and height but instead depend on css for setting the correct width and height.

<picture>
            <source 
              srcset="image-245w.webp 245w, image-332w.webp 332w"
              type="image/webp"
            >
            <img
              srcset="image-245w.png 245w, image-332w.png 332w" 
              class="img-fluid" 
              src="image-245w.png" 
              alt="image" 
              width="245" 
              height="600">     
</picture>
     @media (max-width: 812px) {
        .img-fluid {
           width: 245px;
           height: auto;
        }
      }
      @media (min-width: 813px) {
        .img-fluid {
           width: 332px;
           height: auto;
        }
      }

It is important to understand that the srcSet in combination with sizes (or in combination with the css above) doesn't necessarily force the browser to use one image or the other. Depending on the device pixel ratio the browser may choose to load the bigger image to show in the 245px wide image slot. You just have to trust the browser that it will choose the best file for the job. There is not a one to one relationship between 'sizes' and srcSet. That's why you can provide more (or less) files in a srcSet than there are options in the sizes attribute.

What ever you try to do, its way to complicated thatw ay you try to accoplish it. You can use the very same image and resize it with CSS. Use media queries to declare different sizes of the image depending on the screen width like shown below.

/* displays the image at 245x600px for small screens */

@media only screen
  and (max-width: /*enter a width in px */) {
    .img-fluid {
      height: 245px;
      width: 600px;
    }
}

/* displays the image at 332x812px for small screens */

@media only screen
  and (min-width: /*enter a width of max-width +1px*/) {
    .img-fluid {
      height: 332px;
      width: 812px;
    }
}
      
<img class="img-fluid" src="image-245w.png" alt="image"> 

Related