A picture element to load correctly resized webp images in HTML

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:

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.

The first step: how to load webp images with the picture element?

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` 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.

The complete snippet – how to load a responsive webp image

A more complete snippet that I wrote to load responsive webp images from the Contentful Images API looks like the following.

  <!-- 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:

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.