How to Build Responsive Images with srcset

By Saurabh Kirtani

This article is part of a web development series from Microsoft. Thank you for supporting the partners who make SitePoint possible.

In this connected world where people have a variety of devices, ensuring your website works seamlessly across all of them is a need, not an option. Your website views won’t come from a single resolution device, or a single form-factor. To cater to all of them, your website needs to be responsive.

If you’re familiar with responsive web design (RWD), you probably know the intricacies that come with a responsive design (and how Bootstrap solves a lot of them!). An important part of a responsive website are responsive images. In this article, we’ll learn more about responsive images on the web and see how to build them.

What is a Responsive Image?

In simple terms, a responsive image is an image which is displayed in its best form on a web page, depending on the device your website is being viewed from. The term ‘best form’ could mean multiple things:

  • You want to show a separate image asset based on the user’s physical screen size. For example: you want to show a separate image asset on a 13.5 inch laptop and a 5 inch mobile phone (on a maximized browser).
  • You want to show a separate image based on the resolution of the device (or, the device-pixel ratio, which is the ratio of device pixel and CSS pixel).
  • You want to show an image in a specified image format (JPEG XR, for example) if the browser supports it, probably because of the higher compression that format supports.

The building blocks for responsive are included in most modern browsers including Microsoft Edge (starting Windows Insider Build 10547). You can view the Web Platform status of features like srcset here.

How to Enable Responsive Images

There are a number of ways to enable responsive behavior of images. One of the older methods (not recommended) is by simple scripting, but this leads to a couple of problems. One, if a script determines which image to download, but the script itself is loaded after the images specified in the HTML have been downloaded, you may potentially end up with two downloaded images. Two, if you don’t specify any image in HTML and want to load only the image as defined by the script, you’ll end up with no image at all for the browsers which have scripting disabled.

Hence, we need a better way to deal with responsive images. And thankfully, there is! The recommended way is to use:

  1. srcset attribute
  2. sizes attribute
  3. picture element

Let’s delve a little deeper.

The srcset Attribute:

Before we explore how srcset is actually used, let’s understand a few terms:

Device-pixel ratio

Device-pixel ratio is the number of device pixels per CSS pixel. Two key conditions contribute to device-pixel ratio:

  1. Pixel density of the device (number of physical pixels per inch): A high resolution device will have a higher pixel density and hence, for the same zoom level, it will have a high device-pixel ratio as compared to a lower resolution device.For example: A high-end Lumia 950 phone would have a higher resolution than a budget Lumia 630 phone, and hence it will have a higher device-pixel ratio for the same zoom level.
  2. Zoom level of the browser: For the same device, a higher zoom level means more number of device pixels per CSS pixel, and hence a higher device-pixel ratio. For example, consider this figure:Zoom level of the browserWhen you zoom in on your browser (Ctrl + Plus), the number of CSS pixels for your div remains the same, but the number of device pixels it occupies increases. So, you have a higher number of device pixels per CSS pixel.

When you want to display separate images (or usually, a separate asset of the same image) based on the device-pixel ratio, you’d go with basic srcset implementation:

<img src="images/space-needle.jpg"
srcset="images/space-needle.jpg 1x, images/space-needle-2x.jpg 2x,
images/space-needle-hd.jpg 3x">

x descriptor in the srcset attribute is used to define the device-pixel ratio. Hence,

  1. for a device-pixel ratio of 1, the image space-needle.jpg will be used.
  2. for a device-pixel ratio of 2, the image space-needle-2x.jpg will be used.
  3. for a device-pixel ratio of 3, the image space-needle-hd.jpg will be used.

src attribute is used as a fallback for the browsers which do not yet support srcset implementation.

This works well. Using the x descriptor, you’ll always get the same image on the devices with similar device-pixel ratio – even if this means that you get the same image on a 13.5 inch laptop and a 5 inch mobile phone which have the same device-pixel ratio.

Now suppose we want a different size (height, width) image on a larger or smaller viewport. This is where the w descriptor in srcset and a new attribute – sizes comes into play.

  • w descriptor: This describes the width of the image being referenced. Consider this example:
    <img src="images/space-needle.jpg"
    srcset="images/space-needle.jpg 200w, images/space-needle-2x.jpg 400w,
    images/space-needle-hd.jpg 600w">
  • This mentions that the width of the first image is 200px, second image is 400px, and third image is 600px. Also, if the user’s screen is 150 CSS pixels wide, this equates to the following in terms of x descriptors:
    <img src="images/space-needle.jpg"
    srcset="images/space-needle.jpg 1.33x, images/space-needle-2x.jpg 2.67x,
    images/space-needle-hd.jpg 4x">

    (Remember, device-pixel ratio is just number of device pixels/CSS pixels.)

The sizes Attribute:

The actual implementation where you’d want a different size image (different height, width) on different screen sizes is accomplished by using sizes attribute along with the w descriptor of srcset attribute. Let’s again learn through a couple of examples:

Example 1

Say you want the image to be viewed in half of the viewport width. You’ll type:

<img src="images/space-needle.jpg" sizes="50vw"
srcset="images/space-needle.jpg 200w, images/space-needle-2x.jpg 400w,
images/space-needle-hd.jpg 600w">

The browser will now decide which image to download based on the browser width and the device pixel ratio. For example:

  • If the browser width is 500 CSS pixels, the image will be displayed 250px wide (because of 50vw). Now, this is equivalent to specifying:
    srcset="images/space-needle.jpg 0.8x, images/space-needle-2x.jpg 1.6x,
    images/space-needle-hd.jpg 2.4x"
  • So, for a 1.5x display, images/space-needle-2x.jpg will be downloaded by a browser, since it gives a device-pixel ratio of 1.6x (which is most suitable for a 1.5x display).

Example 2

You want the image to be displayed in half of the viewport width when the viewport width is greater than 40em, but the image should occupy the complete width when the viewport width is less than or equal to 40em. This is how you’ll approach it:

<img src="images/space-needle.jpg" sizes="(max-width: 40em) 100vw, 50vw"
srcset="images/space-needle.jpg 200w, images/space-needle-2x.jpg 400w,
images/space-needle-hd.jpg 600w">

This is very similar to media queries. So, for a viewport which is 39em wide, (max-width: 40em) evaluates to true, which means 100vw, that is, the image is as wide as the viewport. If the viewport is the browser window and if the browser width is 500 CSS pixels, the image will be displayed 500px wide. This is equivalent to specifying:

<img src="images/space-needle.jpg" sizes="(max-width: 40em) 100vw, 50vw"
srcset="images/space-needle.jpg 0.4x, images/space-needle-2x.jpg 0.8x,
images/space-needle-hd.jpg 1.2x">

Similar to above, the browser will decide which image to pick from above for a specific display.

For a viewport which is 41em wide, (max-width: 40em) evaluates to false, which means 50vw, that is, the image is half as wide as the viewport.

The next element we’ll see is the picture element, but before that I need a quick recap :)

Use-case Solution
I want the same image to be displayed across all devices, but I want to display it in a higher resolution on devices which can support it. The height and width of the image should remain fixed. Make multiple assets of the same image (space-needle.jpg, space-needle-hd.jpg). Use srcset with x descriptor.
I want the same scenario as above, but I should be able to customize height and width of the image based on the viewport. Use sizes and srcset with w descriptor (again, make multiple assets of the same image)
I’m doubtful that if I use the same image for a smaller screen size, the primary subject of my image may become too small in size. I want to display a different image (more focused on the primary subject) in a different screen size, but I still want to display separate assets of the same image based on device-pixel ratio, and I want to customize height and width of the image based on viewport. ?

The solution to ‘?’ is picture element!

The picture Element

As we saw above, picture element is used when you want to show a different image depending on the rendered size of the image. The picture element is a container which contains other elements that control the image to be downloaded. Let’s look at an example:

<picture>

<source media="(max-width: 20em)" srcset="images/small/space-needle.jpg 1x,
images/small/space-needle-2x.jpg 2x, images/small/space-needle-hd.jpg 3x">

<source media="(max-width: 40em)" srcset="images/medium/space-needle.jpg 1x,
images/medium/space-needle-2x.jpg 2x, images/medium/space-needle-hd.jpg 3x">

<img src="space-needle.jpg" alt="Space Needle">

</picture>

The first source element whose media attribute’s media query holds true is chosen.
So, if the max width of viewport is 20em, the corresponding image source is selected from the images/small directory, based on the device-pixel ratio. All the images within one srcset are usually multiple assets of the same image.

The picture element itself does not display anything. Even the source element within the picture element does not represent anything of its own. The source element must contain the srcset attribute and it may have sizes, media and type attributes. It’s necessary to add img element within picture. You won’t see any image without the img element. All the source elements within the picture element are just there to feed the image a source.

You can also do an image selection based on the image format supported by the browser. This is especially useful when there are good savings on the image size just based on the format. For example, JPEG-XR, an efficient image format, which usually takes less image size as compared to JPG, is supported by Microsoft Edge and IE9+. Using type attribute within the source element, you can test for this format:

<picture>

<source media="(max-width: 30em)" type="image/vnd.ms-photo"
srcset="images/small/space-needle.jxr 1x, images/small/space-needle-2x.jxr 2x,
images/small/space-needle-hd.jxr 3x">

<source media="(max-width: 30em)" type="image/jpg"
srcset="images/small/space-needle.jpg 1x, images/small/space-needle-2x.jpg 2x,
images/small/space-needle-hd.jpg 3x">

<img src="space-needle.jpg" alt="Space Needle">

</picture>

When it is used, both the attributes: media attribute and the type attribute should resolve to true for that source element to be chosen. If the browser can’t understand any of the formats, it falls back to img.

Putting It All Together

After understanding how responsive images work, let’s look at a complete example which utilizes all the three together – srcset, sizes and picture.

<!DOCTYPE html>
<html>
<head>
<title> Responsive images are here! </title>
</head>

<body style="width:100%">

<picture>
<source media="(max-width: 700px)" sizes="(max-width: 500px) 50vw, 10vw"
srcset="stick-figure-narrow.png 138w, stick-figure-hd-narrow.png 138w">

<source media="(max-width: 1400px)" sizes="(max-width: 1000px) 100vw, 50vw"
srcset="stick-figure.png 416w, stick-figure-hd.png 416w">

<img src="stick-original.png" alt="Human">
</picture>

</body>
</html>

In this example, we’ve used picture which contains multiple source elements. The first one is chosen if the max width is 700px. If this is chosen, the sizes attribute then decides the size of the image to be displayed based on the width breakpoints we have mentioned. The implementation is exactly similar to what we have seen in sizes attribute. So, if the max width is 500px (width ranges from 0px-500px), the image will occupy half of the viewport. And the image source is chosen based on the device-pixel ratio. However, if the viewport width is greater than 500px (but <=700px, because we are within the first source element), then the image will occupy just 1/10 of the viewport.

Similarly, the second source element is chosen if the max width is 1400px (which means now the width ranges from 701px to 1400px). The sizes attribute ensures that if the viewport width ranges from 701px to 1000px, the image’s width is same as the viewport width, and if the viewport width ranges from 1001px to 1400px, the image occupies half of the viewport width. To verify all the cases, I simply resize the browser and check. In real-world use, your website will be accessed through different devices and that is where you’ll see the actual use of responsive images. Here is the output:

(Please note all the image sources and the image width set here are just for the demo, and as you can see, I make some funny stick figures!)

Viewport width set between 1001px and 1400px: the image width is half of the viewport width (Image source being used is stick-figure.png)
Viewport width set between 1001px to 1400px

Viewport width set between 701px and 1000px: the image width is same as the viewport width. (Image source being used is stick-figure.png)
Viewport width set between 701px to 1000px

Viewport width set between 501px and 700px: The image width is 1/10 of the viewport width. (Image source being used is stick-figure-narrow.png)
Viewport width set between 501px to 700px

Viewport width set between 0px and 500px: The image width is half of the viewport width. (Image source being used is stick-figure-narrow.png)
Viewport width set between 0px to 500px

And We’re Almost Done!

Responsive images give you, the developer, an option to give the best experience to your users across the multiple devices they use to view your website. It’s a good time to start integrating it into your website if you haven’t yet.

I hope you had fun reading this! Reach out to me on Twitter if you want to discuss more!

More Hands-on with Web Development

This article is part of the web development series from Microsoft tech evangelists on practical JavaScript learning, open source projects, and interoperability best practices including Microsoft Edge browser and the new EdgeHTML rendering engine.

We encourage you to test across browsers and devices including Microsoft Edge – the default browser for Windows 10 – with free tools on dev.modern.IE:

In-depth tech learning on Microsoft Edge and the Web Platform from our engineers and evangelists:

More free cross-platform tools & resources for the Web Platform:

  • My Uncle Carson just got an awesome year old Cadillac CTS Sedan just by parttime work from a home pc…look at more info on my~ prof!Ie~

    *e

  • PVgr

    Good article, but the title may be misleading, since the article actually covers more than just scrset!

  • Everybody is talking about HTML5 these days and for a good reason. It is the fifth and arguably the best revision of HTML, the language of the World Wide Web. The phrase ‘build once, work everywhere’ is the simplest and the most significant reason why the HTML5 revolution is taking over the world of web and mobile development. If you looking to hire HTML5 developer for your dream project, hire one from our pool of experienced HTML5 developers with expertise in CSS3 for dynamic visual effects and compatibility on all mobile devices.

    Mobileapptelligence [dot] com, is an award winning HTML5 app development company, delivering HTML5 apps and games to global clients.

    • like Phillip answered I cannot believe that you can make $9890 in 4 weeks on the computer.try this website on `my` `prof1le`
      ^jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj

  • Adarsh Prabhu

    Your examples can be quite confusing to some people. At first, you use pixel (200w) units and later on em units (20em, 40em) to measure the viewport.

    This creates inconsistency in your examples. Why not use the same units?

    Also, what do you mean by “1.5x display” in ex. 1 – this is not a standard way to measure screen size? Are you saying that a scale factor of 1.6x (400px) fills the space of 250px better than 0.8x (200px) because the latter would be too small?

    • and on top of what @adarshprabhu:disqus all says, none of the images seem to load, so the confusion is complete!

  • It is possible to make the images links to other pages using the ‘picture’ element?

    • Nevermind. I was wrapping the source element instead of the parent picture element.

  • snyderpa

    The article is very helpful but leaves me with a question: from the last example, what is the relationship between ‘sizes’ and ‘srcest’ — what criteria would cause the browser to download the ‘hd’ image over the other image?

  • And is there kind of error’s events for source and srcset? Onload , onerror?

  • One of the best explanations i’ve read about responsive image. Great article !

  • I want to thank you for such a lucid and well crafted lesson. Piecing together a coding education, I’ve come across every sort of pedagogical method you can imagine; and I found your syle refreshing and interesting.

    Cheers to that little psychological bait/switch you used at the end of your ‘recap’ table.
    Very effective.

  • kapil chhabra

    Amazing Article Really helpful. Thanks for sharing.

Recommended
Sponsors
Get the latest in Front-end, once a week, for free.