A picture element to load correctly resized webp images in HTML
- Published at
- Updated at
- Reading time
- 4min
tl;dr โ I have trouble remembering (and finding) the complete HTML snippet to load responsive webp
images. You (and future me) find the snippet below.
Loading images in the best sizes and formats was always complicated. With the Avif image format around the corner, there is one more format to consider, and shipping images is not getting any easier. Developers have to choose between traditional image formats like png
and jpeg
, the almost mainstream webp
format, the new avif
image format, and many others.
The browser-support of the available options varies greatly:
- traditional image formats (
png
,jpeg
, and others) are fully supported webp
is nearly full browser supportedavif
is only supported in Chrome.
If you decide to ship webp
or even avif
, you have to consider a loading strategy that works for your users and falls back to image variants for browsers that don't support the new and shiny.
Approaches to load new image formats
There are several ways to load the best-supported image format. If you're building a client-side JavaScript application, you could feature detect image format support and dynamically load the correct image. You could also use a serverless edge-function or even a service worker to read the Accept
header of image requests and respond with the best image format (a browser that supports webp
includes an HTTP header that looks like Accept: image/webp,*/*
).
If you prefer an HTML-only solution, you can leverage responsive images and the picture element to load the best image format.
After searching snippets that cover ways to load webp
using the picture
element countless times, I know that there are many materials out there. But unfortunately, very few articles go further than scratching the surface. Most tutorials show only how to load the webp
format and forget the rest.
Let's have a look at the snippet below. It's the first step to load a webp
or avif
image.
<picture>
<!-- load avif if supported -->
<source srcset="img/image.avif" type="image/avif">
<!-- load webp if supported -->
<source srcset="img/image.webp" type="image/webp">
<!-- load in case no `source` format applies
and use attributes for presentation -->
<img src="img/image.jpg" alt="Alt Text!">
</picture>
This snippet loads webp
or avif
images when the browser supports them. Otherwise it loads a jpeg
. Great stuff; job done!
Not so quickly! This picture
element is not taking pixel density, device size, or CSS layout into consideration. When you're ignoring layout and image dimensions and load a 3500px wide image on a phone with a slow connection, no image format in the world will save all the wasted data and computation.
A more complete snippet that I wrote to load responsive webp
images from the Contentful Images API looks like the following. The beauty of using an Image API is that you can upload an image and request different formats using query parameters (fm=webp
automatically converts the image to webp
). ๐
<picture>
<!-- load webp images if supported -->
<source type="image/webp"
srcset="
https://images.ctfassets.net/.../paris.jpg?w=100&fm=webp 100w,
https://images.ctfassets.net/.../paris.jpg?w=200&fm=webp 200w,
..."
sizes="
(max-width: 768px) calc(100vw - 3em),
(max-width: 1376px) calc(50vw - 8em),
550px">
<!-- load traditional supported image format -->
<img
srcset="
https://images.ctfassets.net/.../paris.jpg?w=100&fm=jpg&fl=progressive 100w,
https://images.ctfassets.net/.../paris.jpg?w=200&fm=jpg&fl=progressive 200w,
..."
sizes="
(max-width: 768px) calc(100vw - 3em),
(max-width: 1376px) calc(50vw - 8em),
550px"
src="https://images.ctfassets.net/.../paris.jpg"
alt="Buildings of Paris"
loading="lazy"
decoding="async"
width="1243"
height="1500">
</picture>
This snippet is a mouth full (and I can not say that I enjoy writing this HTML).
The source
elements include not only a type
attribute but also srcset
and sizes
. These two attributes are core parts of responsive images and can be applied to the source element, too. srcset
and sizes
give additional layout and source URL information so that the browser can load properly sized images depending on screen size and other factors. This picture
element loads not only the best format but also images in the best sizes.
Additionally, the img
element includes more attributes. Be aware that the included img
is not only the picture
's fallback solution. The source
elements' job is to augment and provide additional source information about this one included image.
Even though there is a picture
element around the image, you still have to consider image best practices to provide the best user experience:
alt
โ define alternative text for assistive technology or in case the image doesn't loadloading
โ define an image lazy loading strategydecoding
โ define an image decoding strategywidth
/height
โ provide an aspect ratio to avoid layout shifts
Every time I write an element like the one above, I wonder if it has to be that hard. That's so many keystrokes for a problem that could be solved by the web platform itself. Maybe, I'll open a proposal someday, but I bet someone already did that. ๐
Additional resources
Have a look at the following resources to learn more about the best way of loading responsive images.
- Malte Ubl shared ways to optimize images to the extent
- The Next.js framework started abstracting all image best practices in an
Image
component. It's a promising approach for sure!
Pssst... If you like this post, I send a weekly newsletter including all kinds of web development tricks. ๐
Join 5.5k readers and learn something new every week with Web Weekly.