Repository to collect best practices for web images
We can use media
to define a media query as a breakpoint to load a responsive image.
<picture>
<source srcset="image-wide.jpg" media="(min-width: 600px)">
<source srcset="image-ultrawide.jpg" media="(min-width: 1200px)">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>
In the above code, the browser loads the
image.jpg
in mobile version, theimage-wide.jpg
in tablets resolutions or bigger, and theimage-ultrawide.jpg
will be loaded in screen resolutions bigger than 1200px.
We can use sizes
, that allows you to specify the layout width of the image for a list of media conditions.
<img
srcset="image-wide.jpg 600w,
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
image-ultrawide.jpg 1200w"
src="image.jpg" height="300" width="200" alt="Awesome image">
In the above code (like with
media
), the browser loads theimage.jpg
in mobile version, theimage-wide.jpg
in tablets resolutions or bigger, and theimage-ultrawide.jpg
will be loaded in screen resolutions bigger than 1200px.
Usually, the next-gen image formats like
WebP
,AVIF
orJPEG XL
, can optimize image weight while maintaining good image quality.
With the HTML tag <picture>
we can specify the type
in the <source>
tag. The type is the image format, and we can use it to serve modern image formats. The browser will use the first image format that it supports.
<picture>
<source type="image/webp" srcset="image.webp">
<source type="image/jpeg" srcset="image.jpg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>
In the above code, the browser loads the first image format that it can render. E.g. Internet Explorer 11 nor Safari 13 can't load the WebP image format (a next-gen image format), so they will load the
JPEG
image.
<picture>
<source type="image/jxl" srcset="image.jxl">
<source type="image/avif" srcset="image.avif">
<source type="image/webp" srcset="image.webp">
<source type="image/jpeg" srcset="image.jpg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>
In the above code, we list all the modern image formats, from "more optimized" to "less optimized". The browser will show the first image that it can load and render.
We can combine both approaches to serve modern image formats and responsive images to load the best image on each device.
<picture>
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img src="image.jpg" height="300" width="200" alt="Awesome image">
</picture>
In the above code, we have a combination of all modern image formats and the sizes needed. This is an example, as every site or project needs different sizes.
We can use several attributes to improve the Web Performance, aka user experience.
loading
provides a hint to the user agent on how to handle the loading of the image which is currently outside the window's visual viewport. We can set it toeager
(default value) which tells the browser to load the image as soon as the<img>
element is processed, orlazy
that tells the user agent to hold off on loading the image until the browser estimates that it will be needed imminently.decoding
represents a hint given to the browser on how it should decode the image. The values aresync
to decode the image synchronously for atomic presentation with other content,async
to decode the image asynchronously and reduce delay in presenting other content, andauto
(default mode) which indicates no preference for the decoding mode. The browser decides what is best for the user.fetchpriority
represents a hint given to the browser on how it should prioritize the fetching of the image relative to other images. The values arehigh
to fetch the image at a high priority relative to other images,low
to fetch the image at a low priority relative to other images, andauto
(default mode) which indicates no preference for the fetch priority. The browser decides what is best for the user.
<picture>
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img
loading="lazy"
decoding="async"
src="image.jpg"
height="300"
width="200"
alt="Awesome image">
</picture>
In the above code, we have a better code for all the images below the fold (outside the viewport) and the browser does not load these images (according to the threshold)
Improve the image detected as LCP element of the Core Web Vitals
The previous code covers the scenario for all images outside of the viewport. Usually, the LCP metric refers to an image element, so we can iterate to improve it.
<picture>
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jxl">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.avif 600w,
image-ultrawide.avif 1200w"
type="image/avif">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.webp 600w,
image-ultrawide.webp 1200w"
type="image/webp">
<source
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="image-wide.jpg 600w,
image-ultrawide.jpg 1200w"
type="image/jpeg">
<!-- The <img> tag is a fallback image (required in the <picture> tag) -->
<img
fetchpriority="high"
decoding="sync"
src="image.jpg"
height="300"
width="200"
alt="Awesome image">
</picture>
In the above code, we changed the attribute
decoding
tosync
to priorize the decoding, removed the attributeloading
because the default behavior iseager
, so we don't need it, and we add the attributefetchpriority="high"
to indicate to the browser to load the image as soon as possible.
By the way, we don't need to add these attributes to all
<source>
tags, only to<img>
tags.
We see that we need a lot of code to deliver the best user experience ๐.
We can use a CDN Image Service like Cloudinary to remove the part of code that references the image format, as these services serve the best image format supported by the browser.
The browser sends in the HTTP Headers the accept
header value to indicate which content types, expressed as MIME types.
In the screenshot above we see that my current version browser supports
image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;
, so with a service of automatic image format, the service will send me anavif
image format in response.
<picture>
<img
sizes="(min-width: 600px) 100vw, 600px,
(min-width: 1200px) 100vw, 1200px"
srcset="
https://res.cloudinary.com/nucliweb/image/upload/c_scale,f_auto,w_600/v1010101010/demo/image.png 600px,
https://res.cloudinary.com/nucliweb/image/upload/c_scale,f_auto,w_1200/v1010101010/demo/image.png 1200px"
src="https://res.cloudinary.com/nucliweb/image/upload/f_auto/v1010101010/demo/image.png"
loading="lazy"
decoding="async"
height="300"
width="200"
alt="Awesome image">
</picture>
Now we don't need to define the type because this is transparent to us. The service sends the best image format supported by the browser. Notice that the image in the samples is an
PNG
image, yet the browser will load anavif
image format.
- Addy Osmani, for this image and for all the tips that he is sharing with the community.