Use Responsive Images to Give Each User an Image that is “Just Right”

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

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):
responsive-images-desktop-view-1
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:

  1. Improving Performance
  2. 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:

  1. The old standard img element, but adding a srcset attribute, or
  2. The picture element with nested source 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 a 1920w 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 using media 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” links have a media attribute, and these media attributes prevent both images from loading on any device.

  • When using srcset and sizes for an image, you might have an img 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 and sizes 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 use imagesrcset and imagesizes, not just srcset and sizes. 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” links with picture 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 the img element’s src, and that we need to kind of hack the min-width and max-width values of the media 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 the img 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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.