A picture element to load correctly resized webp images in HTML
Tags: webdev
Published: 2021-01-02 15:34:57

---
title: A picture element to load correctly resized webp images in HTML
published: true
description: A `picture` element snippet to load properly resized responsive webp images in browsers that support them.
tags: webdev
canonical_url: https://www.stefanjudis.com/snippets/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](https://jakearchibald.com/2020/avif-has-landed/), 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 supported](https://caniuse.com/webp)
- [`avif` is only supported in Chrome](https://caniuse.com/avif). 

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](https://github.com/Modernizr/Modernizr/blob/master/feature-detects/img/webp.js) 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) 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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) 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.

```html
<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](https://www.contentful.com/developers/docs/references/images-api/) looks like the following.

```html
<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](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images) and [can be applied to the source element, too](https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element). `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 load
- `loading` – [define an image lazy loading strategy](https://web.dev/browser-level-image-lazy-loading/)
- `decoding` – [define an image decoding strategy](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding)
- `width`/`height` – [provide an aspect ratio to avoid layout shifts](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/)

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](https://www.industrialempathy.com/posts/image-optimizations/)
- [The Next.js framework started abstracting all image best practices in an `Image` component. It's a promising approach for sure!](https://nextjs.org/docs/basic-features/image-optimization)