As our range of web-enabled devices continues to spawn new and different screen sizes, the practice of sending the exact same image to all of those devices becomes problematic. The problems include both the size of the image (like displaying a 1900x800px image on a smartphone) and the size of the file (like making users on slow 3G download a 2MB image).
This is where responsive image comes into play.
With careful coding, we can send different images to different screen sizes to help alleviate this problem.
- Intro
- Why should I care about responsive images?
- How do you make images responsive?
- Notes and Options
- Responsive Image CSS
- Reduce CLS with width and height attributes
- Reduce CLS with aspect-ratio
- Provide varying image resolutions
- Suggest varying sizes using the sizes attribute
- Provide varying image formats
- Optimize page load using Priority Hints
- Optimize page load using Resource Hints
- Optimize page load using the loading attribute
- Optimize page load using the decoding attribute
- Resources
Intro
What does “responsive images” mean? The idea of responsive images is to serve different images to differently sized devices.
But first, responsive design…
Responsive images is actually part of a larger concept called responsive design. Responsive design means that a website’s design should “respond” to the device it is loading into.
For example, if you look at a website on your large desktop monitor, you might want to see large images, large headlines, text spreading across the full screen, something like this (desktop view, 1200px wide):
In the above example, the image is large, 1920x920px, and weighs 225kb.
But if you look at the same website on your smartphone, you wouldn’t want that exact design scrunched into the width of your smartphone, right (mobile view, 390px wide)?
Instead, you would want the website to “respond” to the device you are using, and you would want it look something more like this:
That’s a lot easier to read and interact with!
Let’s look at the two layouts, side-by-side (mobile, desktop):
Note that we still have the same headline (“Spring Cleaning”), the same copy (“Put your best face forward…”), the same CTA (“Shop now”) and even a very similar image.
But the layout is quite different on the smaller screen: a lot less whitespace; the copy below the headline is broken into multiple lines; the CTA stretches the width of the screen; the product information covers the product image more; and the image is cropped quite differently.
And that’s the concept behind responsive design: same HTML, but different device, so different layout.
And now, responsive images
Now let’s take a look specifically at the image in both of these screenshots (mobile, desktop):
Notice how the mobile image is simply a crop of the desktop image? The mobile version has a lot less white space, crops off some of the top two products, has a little more of the lower product.
The same HTML has responded to the device size, serving two completely different images are served to the user:
- Mobile gets an image that is 375x667px
- Desktop gets an image that is 1920x920px
Why should I care about responsive images?
The goal of responsive images is to serve different images to different devices.
There are two main reasons why we would want to do this:
- Improving Performance
- Controlling Design
Improving Performance
This approach uses different sizes of the exact same image, like this:
Note how all 3 of the images above are identical, except for the physical size.
This is important because smaller images mean smaller files and smaller files download faster.
There is no reason to serve a 1920x920px image that weighs 225kb to a screen size than can only display a max width of 380px.
By sending smaller images to smaller devices, the page loads faster, so the user seeing our products faster, resulting in a more enjoyable shopping experience.
And hopefully that corresponds to more sales.
This approach is easiest because you are only making smaller versions of the exact same image.
Controlling Design (“Art Direction”)
This approach, also known as “Art Direction”, uses varying curations of the same image, like this:
Note how each of these 3 images is based on the same image, but are all slightly different:
- the first version shows the full height of all three bottles, includes some white space above and below them and has the reflective look on the right;
- the next version is similar, but loses some of the white space above and below the bottles and has a squarer aspect ratio than the original;
- the last version loses even more white space above and below the bottles, and also crops the image on both the left and right side.
Again, each smaller image means a smaller file, and smaller files download faster.
But in this situation we also changed the focus of each image, losing some of what is less important, the white space, and retaining the focus on what is important, the product.
This approach is slightly more difficult, because you have to explicitly crop each version based on what is important and what is not, and create new versions of the image for each transition in your design.
Which is right?
Neither is right or better, they are simply different approaches to the same goal: reduce the image size for smaller screens.
In the former, design decided the exact same image worked fine in multiple screen sizes.
In the latter, design decided the product focus was important and chose to use different curations.
In both cases, the user with a smaller screen size received smaller images, and therefore loaded our site faster, and was able to peruse our site faster.
How do you make images responsive?
Here is where things get a little more complicated… :-)
There are two different HTML elements that we can use to create responsive images:
- The old standard
img
element, but adding asrcset
attribute, or - The
picture
element with nestedsource
elements.
These two elements loosely align with the two approaches above: Option 1 is typically used for serving different sizes of the same image (the Improve Performance case above), and option 2 is typically used for serving different crops of the same images (the Control Design case above).
There are a lot of options and variations for both elements. To keep this article readable, we will focus on one variation of each element (img
and picture
), and only two image variations for each element (assume “small screen” changes to “large screen” at 768px). Check out the Notes and Options section below for more.
Using the img
element
A basic version of the img
element with the srcset
attribute would look something like this:
<img srcset="/image-mobile.jpg 375w, /image-desktop.jpg 1920w" src="/image-mobile.jpg" alt="Descriptive text" >
The src
and alt
attributes should seem familiar, nothing new there.
But that srcset
looks interesting:
- You should see two URLs (one name ending with “mob”, the other ending with “pc”),
- separated by a comma,
- but also a
375w
after the “mob” version and a1920w
after the “pc” version.
The 375w
and 1920w
are called x-descriptors. They tell the browser the intrinsic width of each image, but note that we use “w” not “px” or “vw”, which you might be more familiar with.
You can have as many URLs and x-descriptors as you want to create and list.
The browser will use whatever information you provide to determine which image is the best one to load into the viewport.
In this way, we just provide information and let the browser do some work for us.
Using the picture element
A basic version of the picture
element with source
elements would look something like this:
<picture> <source srcset="/media/.../SS23_HydrateLips_mob.jpg" media="(max-width: 767px)"> <source srcset="/media/.../SS23_HydrateLips_pc.jpg" media="(min-width: 768px)"> <img src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil"> </picture>
Most of this may seem totally foreign, but buried deep inside you will see a standard img
element, which should seem familiar.
Wrapping that img
element is a picture
element, and nested with that img
element are two source
elements, one for each version of the image (“mob” and “pc”).
Note that each source
element has a srcset
attribute that contains the image URL, and that we are explicitly telling the browser that we want to switch images at 768px wide
Notes and Options
Responsive Image CSS
Regardless of whether you choose the srcset
or picture
option, all responsive images should have this basic CSS:
img { max-inline-size: 100%; // formerly we used width: 100%; block-size: auto; // formerly we used height: auto; }
Reduce CLS with width
and height
attributes
Ideally all images would have width
and height
attributes, similar to this:
<img src="/media/.../SS23_HydrateLips.jpg" width="1920" height="920" alt="Soothing Cleansing Oil" >
Coupled with the above Responsive Image CSS, the browser will use the width and height attributes more as “guides” and will adjust the actual width and height to fit the current screen.
Meaning, if the viewport is only 1200 pixels wide, the above image will fill that screen width and the height will be automatically adjusted by the browser to 575px in order to be the same aspect ratio (48 : 23).
In this way, the browser can “reserve” space for the image while it is being fetched.
This will prevent the image from contributing to CLS.
Reduce CLS with aspect-ratio
If you are not able to provide width
and height
attributes for images, as is the case for most ELC sites, providing the aspect-ratio of the image will also allow the browser to reserve space for the image while it is being fetched.
This will prevent the image from contributing to CLS.
Provide varying image resolutions
Earlier we referenced the image’s intrinsic width as x-descriptors within the srcset
(such as 375w
or 1920w
) so that browser could choose the best version of the image to serve the user.
You can also reference the image’s resolution (1x
, 2x
, 3x
) so the browser can do the same for high-resolution devices.
<img srcset="/media/.../SS23_HydrateLips_1x.jpg 1x, /media/.../SS23_HydrateLips_2x.jpg 2x" src="/media/.../SS23_HydrateLips_1x.jpg" alt="Soothing Cleansing Oil" >
Above, the first item in the srcset
attribute will be served to low-resolution devices (1x
), while the second will be served to high-resolution devices (2x
), such as Retina devices.
Suggest varying sizes using the sizes
attribute
Earlier we used the srcset
attribute to provide multiple image sizes for the browser to choose from. In that example the browser decides which image to serve the user.
If we would like a little more control over which is image is used and when, we can also add a sizes
attribute, which would look something like this:
<img srcset="/media/.../SS23_HydrateLips_mob.jpg 375w, /media/.../SS23_HydrateLips_pc.jpg 1920w" sizes="(max-width: 300px) 100vw, 768px" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil" >
Like the srcset
attribute, the sizes
attribute is a comma-separated list. The two lists correspond to one another, meaning the first item in both lists go together, the second in both lists go together, etc.
In the above example, we are recommending to the browser that the first image in the srcset
attribute (“_mob”) be used for devices with a maximum width of 300 pixels wide, and we state that this image will take the entire screen width.
Provide varying image formats
It is possible to offer multiple image formats using the picture
element, similar to this:
<picture> <source type="image/avif" srcset="/media/.../SS23_HydrateLips.avif"> <source type="image/webp" srcset="/media/.../SS23_HydrateLips.webp"> <img src="/media/.../SS23_HydrateLips.jpg" alt="Soothing Cleansing Oil"> </picture>
In the above example, each source
element gets a type
attribute that tells the browser that image’s format. In this example, the first is an AVIF and the second is a WEBP.
The browser can then determine what image formats it supports and decide which is best to serve the user.
Meaning, in the above example, if the browser supports AVIF, it will fetch that. If not, it will check for WEBP support and fetch that if supported. If not, it will fallback to the JPG that is the src
the img
element.
Optimize page load using Priority Hints
When browsers parse HTML, they assign a different fetch priority to different asset types (CSS and JS are natively a higher priority than images, for example).
To optimize the page load experience, we can “suggest” to the browser that it increase the priority for some assets, such as LCP images.
We can do so by adding a fetchprority="high"
attribute and value to the img
element, such as these examples:
<img srcset="/media/.../SS23_HydrateLips_mob.jpg 375w, /media/.../SS23_HydrateLips_pc.jpg 1920w" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil" fetchprority="high" >
<picture> <source srcset="/media/.../SS23_HydrateLips_mob.jpg"> <source srcset="/media/.../SS23_HydrateLips_pc.jpg" media="(min-width: 768px)"> <img fetchprority="high" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil"> </picture>
Optimize page load using Resource Hints
Resource Hints allow us to give the browser “hints” about what is coming later in the page.
This is especially powerful for requesting fonts and images before the browser might otherwise find them, so they are hopefully ready when the browser does find them.
With regards to LCP images, we want to tell the browser to “preload” that image, ideally before it starts downloading all of the site CSS and JS files.
A basic version of an image “preload” might look something like this:
<link rel="preload" as="image" href="/media/.../SS23_HydrateLips_mob.jpg">
Note the rel
attribute, which tells the browser it should “preload” this asset, the as
attribute which defines the asset as an “image”, and finally the href
attribute which tells the browser the URL of the image it preload.
When dealing with responsive images, this too can get complicated:
- When using completely different markup for different devices, including completely different
img
elements, you are ideally usingmedia
attributes to determine which markup gets shown on different device sizes.
In this case, you would also use a different “preload”link
for each image, similar to this:<link rel="preload" as="image" href="/media/.../SS23_HydrateLips_mob.jpg" media="(max-width: 768px)"> <link rel="preload" as="image" href="/media/.../SS23_HydrateLips_pc.jpg" media="(min-width: 769px)">
Note that both “preload”
link
s have amedia
attribute, and thesemedia
attributes prevent both images from loading on any device. - When using
srcset
andsizes
for an image, you might have animg
element similar to this:<img srcset="/media/.../SS23_HydrateLips_mob.jpg 375w, /media/.../SS23_HydrateLips_pc.jpg 1920w" sizes="(max-width: 300px) 100vw, 768px" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil" >
You then also need to include the
srcset
andsizes
information in the “preload”link
, like this:<link rel="preload" as="image" href="/media/.../SS23_HydrateLips_mob.jpg" imagesrcset="/media/.../SS23_HydrateLips_mob.jpg 375w, /media/.../SS23_HydrateLips_pc.jpg 1920w" imagesizes="(max-width: 300px) 100vw, 768px" >
Note that, in the “preload”
link
, we useimage
srcset
andimagesizes
, not justsrcset
andsizes
. Otherwise, the attribute values are exactly the same. -
When using a
picture
element, you might have something like this:<picture> <source srcset="/media/.../SS23_HydrateLips_small.jpg" media="(max-width: 500px)"> <source srcset="/media/.../SS23_HydrateLips_medium.jpg" media="(max-width: 768px)"> <img src="/media/.../SS23_HydrateLips_large.jpg" alt="Soothing Cleansing Oil"> </picture>
But “preload”
link
s withpicture
elements do not currently work as expected; you need to create a separate “preload”link
for each image option, similar to this:<link rel="preload" as="image" href="/media/.../SS23_HydrateLips_small.jpg" media="(max-width: 500px)"> <link rel="preload" as="image" href="/media/.../SS23_HydrateLips_medium.jpg" media="(min-width: 500.01px) and (max-width: 768px)"> <link rel="preload" as="image" href="/media/.../SS23_HydrateLips_large.jpg" media="(min-width: 768.01px)">
Note that there is a “preload”
link
for each image option, including the one in theimg
element’ssrc
, and that we need to kind of hack themin-width
andmax-width
values of themedia
attribute for each “preload”link
. -
As mentioned in the Priority Hints section, we can suggest that the browser increase the fetch priority of an image by adding a
fetchprority="high"
attribute and value, with the intent of downloading something like an LCP image, sooner than the browser would normally do that.However, rather than attaching the
fetchprority="high"
attribute and value to theimg
element, which would have very little effect, it is recommended that we add it to the “preload”link
, similar to this:<link rel="preload" as="image" href="/media/.../SS23_HydrateLips_mob.jpg" fetchprority="high">
This attribute and value can be added to any of the “preload”
link
examples above.
Optimize page load using the loading
attribute
Natively, when a browser encounters an image, it starts downloading it automatically. If the page has numerous images, this can make the page load slowly.
To optimize the page load experience, we can suggest the browser delay loading some assets, like images, until the user scrolls close to them.
This technique is referred to as lazy loading.
We do so by adding a loading="lazy"
attribute and value to the img
element, such as these examples:
Optimize page load using the decoding
attribute
Natively, when a browser downloads an image, it starts decoding it automatically, synchronously. If the page has numerous images, this can make the page load slowly.
To optimize the page load experience, we can suggest the browser decode some images asynchronously.
We do so by adding a decoding="async"
attribute and value to the img
element, such as these examples:
<img srcset="/media/.../SS23_HydrateLips_mob.jpg 375w, /media/.../SS23_HydrateLips_pc.jpg 1920w" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil" loading="lazy" >
<picture> <source srcset="/media/.../SS23_HydrateLips_mob.jpg"> <source srcset="/media/.../SS23_HydrateLips_pc.jpg" media="(min-width: 768px)"> <img loading="lazy" src="/media/.../SS23_HydrateLips_mob.jpg" alt="Soothing Cleansing Oil"> </picture>
Resources
- https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
- https://web.dev/learn/design/responsive-images/
- https://cloudfour.com/thinks/responsive-images-101-definitions/
- https://css-tricks.com/a-guide-to-the-responsive-images-syntax-in-html/
- https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/
- https://caniuse.com/srcset
- https://caniuse.com/picture
- https://caniuse.com/?search=resource%20hints
- https://web.dev/preload-responsive-images/
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority
- https://web.dev/priority-hints/#browser-priority-and-fetchpriority