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 webpage, 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:
srcset
attribute sizes
attribute picture
element
Let’s delve a little deeper.
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:
When 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,
- for a device-pixel ratio of 1, the image space-needle.jpg will be used.
- for a device-pixel ratio of 2, the image space-needle-2x.jpg will be used.
- 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.)
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!
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 701px and 1000px: the image width is same as the viewport width. (Image source being used is stick-figure.png)
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 0px and 500px: The image width is half of the viewport width. (Image source being used is stick-figure-narrow.png)
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 and engineers 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.microsoftedge.com:
More in-depth learning from our engineers and evangelists:
Our community open source projects:
More free tools and back-end web dev stuff: