Prescriptive syntaxes

The <picture> element doesn't render anything on its own, but instead acts as a decision engine for an inner <img> element, telling it what to render. <picture> follows a precedent already set by the <audio> and <video> elements: a wrapper element that contains individual <source> elements.

   <source …>
   <source …>
    <img …>
</picture …>

That inner <img> also provides you with a reliable fallback pattern for older browsers without support for responsive images: if the <picture> element isn't recognized by the user's browser, it's ignored. The <source> elements are then discarded as well, since the browser either won't recognize them at all, or won't have meaningful context for them without a <video> or <audio> parent. The inner <img> element will be recognized by any browser, though—and the source specified in its src will be rendered as expected.

"Art directed" images with <picture>

Making changes to the content or aspect ratio of an image based on the size of the image in the page is typically referred to as "art directed" responsive images. srcset and sizes are designed to work invisibly, seamlessly swapping out sources as the user's browser dictates. There are times, however, where you want to alter sources across breakpoints to better highlight the content, the same way you adapt page layouts. For example: a full-width header image with a small central focus may work well on a large viewport:

A header width image of a periwinkle flower surrounded by leaves and stems, being visited by a honeybee.

But when scaled down to suit small viewports, the central focus of the image might be lost:

A header width image of a periwinkle flower, scaled down. The honeybee is barely visible.

The subject of these image sources are the same, but in order to better focus on that subject visually, you'll want the proportions of the image source to change across breakpoints. For example, a tighter zoom on the center of the image, and some of the detail at the edges cropped out:

A zoomed in crop of the periwinkle flower.

That sort of "cropping" can be achieved through CSS, but would leave a user requesting all the data that makes up that image, even though they might never end up seeing it.

Each source element has attributes defining the conditions for the selection of that source: media, which accepts a media query, and type, which accepts a media type (previously known as "MIME type"). The first <source> in the source order to match the user's current browsing context is selected, and the contents of the srcset attribute on that source will be used to determine the right candidates for that context. In this example, the first source with a media attribute that matches user's viewport size will be the one selected:

  <source media="(min-width: 1200px)" srcset="wide-crop.jpg">
  <img src="close-crop.jpg" alt="…">

You should always specify the inner img last in the order—if none of the source elements match their media or type criteria, the image will act as a "default" source. If you're using min-width media queries, you want to have the largest sources first, as seen in the preceding code. When using max-width media queries, you should put the smallest source first.

   <source media="(max-width: 400px)" srcset="mid-bp.jpg">
   <source media="(max-width: 800px)" srcset="high-bp.jpg">
   <img src="highest-bp.jpg" alt="…">

When a source is chosen based on the criteria you've specified, the srcset attribute on source is passed along to the <img> as though it were defined on <img> itself—meaning you're free to use sizes to optimize art directed image sources as well.

   <source media="(min-width: 800px)" srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w">
   <source srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w">
   <img src="fallback.jpg" alt="…" sizes="calc(100vw - 2em)">

Of course, an image with proportions that can vary depending on the selected <source> element raises a performance issue: <img> only supports a single width and height attribute, but omitting those attributes can lead to a measurably worse user experience. In order to account for this, a relatively recent—but well supported—addition to the HTML specification allows for use of height and width attributes on <source> elements. These work to reduce layout shifts just as well as they do on <img>, with the appropriate space reserved in your layout for whatever <source> element is selected.

      media="(min-width: 800px)"
      srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w"
   <img src="fallback.jpg"
      srcset="lower-bp-1200.jpg 1200w, lower-bp-800.jpg 800w"
      sizes="calc(100vw - 2em)"

It's important to note that art direction can be used for more than decisions based on viewport-size—and it should, given that the majority of those cases can be more efficiently handled with srcset/sizes. For example, selecting an image source better suited to the color scheme dictated by a user's preference:

   <source media="(prefers-color-scheme: dark)" srcset="hero-dark.jpg">
   <img srcset="hero-light.jpg">

The type attribute

The type attribute allows you to use the <picture> element's single-request decision engine to only serve image formats to browsers that support them.

As you learned in Image Formats and Compression, an encoding that the browser can't parse won't even be recognizable as image data.

Before the introduction of the <picture> element, the most viable front-end solutions for serving new image formats required the browser to request and attempt to parse an image file before determining whether to throw it away and load a fallback. A common example was a script along these lines:

   <img src="image.webp"
    onerror="this.src=this.getAttribute('data-fallback'); this.onerror=null;"

With this pattern, a request for image.webp would still be made in every browser—meaning a wasted transfer for browsers without support for WebP. Browsers that couldn't then parse the WebP encoding would throw an onerror event, and swap the data-fallback value into src. It was a wasteful solution, but again, approaches like this one were the only option available on the front-end. Remember that the browser begins making requests for images before any custom scripting has a chance to run—or even be parsed—so we couldn't preempt this process.

The <picture> element is explicitly designed to avoid those redundant requests. While there's still no way for a browser to recognize a format it doesn't support without requesting it, the type attribute warns the browser about the source encodings up-front, so it can decide whether or not to make a request.

In the type attribute, you provide the Media Type (formerly MIME type) of the image source specified in the srcset attribute of each <source>. This provides the browser with all the information it needs to immediately determine whether the image candidate provided by that source can be decoded without making any external requests—if the media type isn't recognized, the <source> and all its candidates are disregarded, and the browser moves on.

 <source type="image/webp" srcset="pic.webp">
 <img src="pic.jpg" alt="...">

Here, any browser that supports WebP encoding will recognize the image/webp Media Type specified in the type attribute of the <source> element, select that <source>, and—since we've only provided a single candidate in srcset—instruct the inner <img> to request, transfer, and render pic.webp. Any browser without support for WebP will disregard the source, and absent any instructions to the contrary, the <img> will render the contents of src as it has done since 1992. You don't need to specify a second <source> element with type="image/jpeg" here, of course—you can assume universal support for JPEG.

Regardless of the user's browsing context, all of this is achieved with a single file transfer, and no bandwidth wasted on image sources that can't be rendered. This is forward-thinking, as well: as newer and more efficient file formats will come with Media Types of their own, and we'll be able to take advantage of them thanks to picture—with no JavaScript, no serverside dependencies, and all the speed of <img>.

The future of responsive images

All of the markup patterns discussed here were a heavy lift in terms of standardization: changing the functionality of something as established and central to the web as <img> was no small feat, and the suite of problems those changes aimed to solve were extensive to say the least. If you've caught yourself thinking that there's a lot of room for improvement with these markup patterns, you're absolutely right. From the outset, these standards were intended to provide a baseline for future technologies to build on.

All of these solutions have necessarily depended on markup, so as to be included in the initial payload from the server, and arrive in time for the browser to request image sources—a limitation that led to the admittedly unwieldy sizes attribute.

However, since these features were introduced to the web platform, a native method of deferring image requests was introduced. <img> elements with the loading="lazy" attribute aren't requested until the layout of the page is known, in order to defer requests for images outside of the user's initial viewport until later on in the process of rendering the page, potentially avoiding unnecessary requests. Because the browser fully understands the page layout at the time these requests are made, a sizes="auto" attribute has been proposed as an addition to the HTML specification to avoid the chore of manually-written sizes attributes in these cases.

There are also additions to the <picture> element on the horizon as well, to match some exceptionally exciting changes to the way we style out page layouts. While viewport information is a sound basis for high-level layout decisions, it prevents us from taking a fully component-level approach to development—meaning, a component that can be dropped into any part of a page layout, with styles that respond to the space that the component itself occupies. This concern led to the creation of container queries: a method of styling elements based on the size of their parent container, rather than the viewport alone.

While the container query syntax has only just stabilized—and browser support is very limited, at the time of writing—the addition of the browser technologies that enable it will provide the <picture> element with a means of doing the same thing: a potential container attribute that allows for <source> selection criteria based on the space the <picture> element's <img> occupies, rather than based on the size of the viewport.

If that sounds a little vague, well, there's a good reason: these web standards discussions are ongoing, but far from settled—you can't use them just yet.

While responsive image markup promises to only get easier to work with over time, like any web technology, there are a number of services, technologies, and frameworks to help ease the burden of hand-writing this markup available. In the next module, we'll look at how to integrate everything we've learned about image formats, compression, and responsive images into a modern development workflow.