Responsive images for better performance

Reading time

~ 5 min read
Tags

css, html
Author

Mustapha Aouas
Responsive images for better performance

In an era where web content is consumed on devices ranging from smartwatches to 4K monitors, mastering responsive images is crucial for web developers. This article delves into the art of delivering the perfect image for every screen size and pixel density, unlocking the power of the srcset and sizes attributes to boost performance and enhance user experience.

While implementing responsive images might seem daunting at first, it's a skill well worth mastering. We'll explore how a bit of extra markup can dramatically improve your site's performance and appearance across devices. We'll also tackle the challenges of testing responsive images and the subtle art of balancing HTML structure with CSS styling.

By the end of this guide, you'll have the knowledge to implement responsive images confidently, ensuring your website looks crisp and loads quickly on any device. Plus, stick around for a special bonus that will take your responsive image game to the next level. Ready to revolutionize how you handle images on the web? Let's dive in!

The srcset and sizes attributes

The responsive image spec boils down to two tags and two attributes. The first tag we’re going to look at is our good old <img> tag. Two new attributes have been added to the <img> tag: srcset and sizes. These two attributes tell the browser what resolutions are available for a given image, and what size the image is supposed to be displayed at, at any given breakpoint. Based on this information, the browser is able to choose and download the most optimal image file for any given device.

Lets start by having a look at the srcset attribute.

The srcset attribute

<img srcset="/assets/images/image-tiny.jpeg 150w,
             /assets/images/image-small.jpeg 300w,
             /assets/images/image-medium.jpeg 600w,
             /assets/images/image-large.jpeg 1000w,
             /assets/images/image-extra-large.jpeg 1500w"

     src="/assets/images/image-medium.jpeg"
     alt="Banner image">

The srcset attribute contains a comma-separated list of URLs with a w-descriptor, informing the browser how wide each source image is. One awesome thing about this syntax is that we don’t need to tell the browser what images to use for the different pixel density displays, and what images to use for the different screen sizes. The browser is able to pick the best image size on its own. Note that the scr attribute is here as a fallback for browsers that does not support srcset (IE 😒).

In the snippet above, with the srcset attribute we provide five image sources with different sizes: 150px, 300px, 600px, 1000px, and 1500px. And the browser will choose the best image depending on screen size and pixel density. Have a look at the video below:

images fetched by the browser

And if we open the network tab we see that the images were fetched when needed:

Network tab

That makes for better performance because the images will load quickly on smaller devices, and it therefor makes a better user experience.

 

In the example above the image width was 100vw, what if we had a layout where the width of the image was 50vw (minus some padding) on desktop and 100vw (minus some padding) on mobile? That's what sizes is for.

The sizes attribute

While the srcset attribute describes the actual width of the source files, the sizes attribute tells the browser how wide the image is supposed to be displayed on the screen. This is done with the use of inline media queries. They work exactly the same as in CSS (without the curly braces), except you only use it to describe the displayed width of the image. Let's have a look:

<img srcset="/assets/images/image-tiny.jpeg 150w,
             /assets/images/image-small.jpeg 300w,
             /assets/images/image-medium.jpeg 600w,
             /assets/images/image-large.jpeg 1000w,
             /assets/images/image-extra-large.jpeg 1500w"

     sizes="(max-width: 700px) calc(100vw - 10px),
                               calc(50vw - 10px)"

     src="/assets/images/image-medium.jpeg"
     alt="A photo of Lyon city">

In the first line (of the sizes attribute) we specified the width of the image when the screen width is less than 700px. Then, in the second line we specified the default width (so in this case, devices with a screen size that is bigger that 700px).

With this, the browser is aware of the width of the image in our layout and is able to decide what is the best file to load. Have a look at the result:

Image responsiveness with layout

Note that the order of the image-URLs in the srcset attribute doesn’t matter. The order in the sizes attribute, however, matters a lot! The browser will use the first media query that matches the current state of the browser.

This is amazing, but there's more. Now let's say we want to use other images if the user prefers dark themes. For that, we can use the <picture> tag. Let's dig in.

Picture

You can think of the <picture> element as a group of image sources. Each individual <source> work much like a single <img> tag, but with an added media attribute. This allows us to specify different groups of sources for different media queries, while still benefitting from the browser's ability to choose the most optimal resolution of a given image.

Let's use that to specify two sources, one for a light theme and one for a dark theme:

<picture>
  <!-- Dark theme -->
  <source media="(prefers-color-scheme: dark)"
          srcset="/assets/images/tiny-dark.png 150w,
                  /assets/images/small-dark.png 300w,
                  /assets/images/medium-dark.png 600w,
                  /assets/images/large-dark.png 1000w,
                  /assets/images/xl-dark.png 1500w"

          sizes="(max-width: 700px) 100vw, 50vw">
    <!-- Light theme -->
    <source srcset="/assets/images/tiny.png 150w,
                    /assets/images/small.png 300w,
                    /assets/images/medium.png 600w,
                    /assets/images/large.png 1000w,
                    /assets/images/xl.png 1500w"
            sizes="(max-width: 700px) 100vw, 50vw">

  <img src="/assets/images/medium.png" alt="Banner image">
</picture>

That looks like this in the browser:

Image responsiveness with layout dark mode

To emulate dark mode: command + shift + p then paste "prefers-color-scheme" and select light or dark.

Note that the picture element itself does not display anything, it provides a context for its contained <img> element that enables it to choose from multiple URLs. That means that you should always add an <img> tag inside the the <picture> tag to specify the alt attribute and loading="lazy" if you want to lazy load the image.

Wrap up

Throughout this article, we've explored the powerful duo of srcset and sizes attributes, unlocking their potential with both the <img> and <picture> tags. We've dissected when and why to use each approach, equipping you with the knowledge to make informed decisions in your projects.

Key Takeaways:

By embracing these responsive image techniques, we're not just optimizing for various devices – we're respecting our users' time, data, and overall experience. No more forcing mobile users to download oversized images on limited connections or making desktop users squint at blurry photos 😃.

Next Steps:

Audit your existing projects and identify opportunities to implement responsive images. Experiment with different srcset and sizes configurations to find the sweet spot for your content.

Share your experiences and insights with the web development community.

Thank you for investing your time in mastering this crucial aspect of modern web development. If you found this article valuable, I encourage you to share it with your network and continue the conversation. For more web development insights and tips, follow me on Twitter @theAngularGuy. Let's keep pushing the boundaries of what's possible on the web! Happy coding, and may your images always be crisp, quick to load, and perfectly sized for every screen!