<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
<video> elements: a wrapper element that contains individual
<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
<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
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.
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:
But when scaled down to suit small viewports, the central focus of the image might be lost:
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:
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.
source element has attributes defining the conditions for the selection of that
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
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
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
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.
srcset="high-bp-1600.jpg 1600w, high-bp-1000.jpg 1000w"
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
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">
type attribute #
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:
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.
<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.
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
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
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
<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.