Introduction
Making sure that your images look excellent on all screen sizes could prove to be a complex undertaking since you must consider the image's size, location, how much of the image is visible, the user's connection speed, and many other factors. Most developers end up using the same image for all screen widths and letting the browser scale it to fit. This is a terrible practice since the browser will continue to download the full-size image (which is typically rather huge) even if it is only displayed at a fraction of its original size. This wastes your users' bandwidth and drastically slows down your page load times (especially on slower connections).
The solution to this issue is to use responsive pictures. Responsive images are those that are tailored to the user's screen size. This means that the image will be downloaded in the appropriate size and resolution for the user's device. This will dramatically minimize the amount of data delivered to the user, resulting in faster page load times. There are numerous approaches to implementing responsive images, ranging from easy to sophisticated, and in this article, I will demonstrate all of them.
Methods
To ensure your images are fully responsive and optimized for different screen sizes, here are three powerful methods you should know as a web developer. These methods will not only improve the visual quality of your images but also enhance the overall performance of your website.
img
srcset
Attributeimg
sizes
Attributepicture
Element
img
srcset
Attribute
By far the simplest way to implement responsive pictures is to use the img tag's srcset attribute. This feature allows you to specify numerous distinct picture sizes, and the browser will automatically select the image that best fits the user's screen size.
<img
src="house-1200.png"
alt="A house"
srcset="house-400.png 400w, house-800.png 800w, house-1200.png 1200w"
/>
This code may appear confusing or not, so let me explain exactly what is going on. To get started, we have the standard src
and alt
attributes that you are accustomed to with all photos. If your user's browser does not support srcset
, the image will be served from the src
url. This is quite unlikely, given that srcset
has been supported by all major browsers for atleast 10 years.
This somewhat confusing srcset
attribute allows a comma-separated list of image URLs and widths. The first item in the list, house-400.png 400w
, has the URL house-400.png
. The name of this link is irrelevant, however if you have multiples of the same image at different sizes, you should name them with the size in the name.
The second element in this item is 400w
. This is presumably puzzling since w
is not a CSS unit; rather it represents the image's intrinsic width in pixels. This is the image file's real width. You can simply determine this width by viewing the image in your file browser/explorer. If you're using Windows, right-click the image and select Properties
; on a Mac, there should be an option named Get Info
. In this situation, the image is 400 pixels wide, so we set the width to 400w
.
The browser will then use this information to automatically select which image to download. For example, if the user's screen is less than 400px wide, it will use the house-400.png
image, which is the smallest image that can be utilized without pixel stretching/blurring. When the browser's width exceeds 400 pixels, it will transition to the house-800.png
image. This is because the 400px image is now smaller than the current screen size and will appear stretched/blurred if used. This would continue across all screen sizes until the browser reached the largest image provided.
This is excellent because the browser will now download a reduced image on small screen sizes while maintaining a high resolution image on large screen sizes. This will dramatically minimize the amount of data delivered to the user, resulting in faster page load times. This is an example of how it looks. Try adjusting your browser size to a tiny size and then reloading the page to see whether the smaller image was downloaded.
<img
style="width: 100%; border-radius: 1rem;"
src="https://warehouse.co/3200x800/png"
srcset="
https://warehouse.co/800x200/png 800w,
https://warehouse.co/1600x400/png 1600w,
https://warehouse.co/3200x800/png 3200w
"
/>
window.devicePixelRatio
.Handling Different Pixel Densities
When you have a picture that will always be the same size on the screen, you want to ensure that it appears nice on high-resolution screens. For example, if you just submit a 100px wide image for your logo, it will appear blurry on high-resolution devices. To handle this, you can use the srcset
attribute to offer numerous images of sizes, where the x
unit denotes pixel density.
<img
src="logo-200.png"
alt="my Logo"
srcset="logo-100.png 1x, logo-150.png 1.5x, logo-200.png 2x"
/>
The above code is fairly similar to our last srcset
example, with the main difference being that we use units such as 1.5x
and 2x
rather than hard-coded pixel values. This metric relates to the screen's pixel density. For example, if a user's screen has a pixel density of 1.25 device pixels per CSS pixel, the logo-150.png
picture will be utilized because it is the smallest image that can be used without stretching or blurring pixels.
1x
unit, as that is set by default. If you only have two images, use logo-100.png
, logo-200.png
2x
rather than logo-100.png
1x,
logo-200.png
2x.
img
sizes
Attribute
What we've discussed so far is the most basic method for implementing responsive photos, but in many cases, the image size does not match the width of your screen. This article is an excellent illustration of that. On small screen sizes, the content on my blog (including images) takes up the entire width of the screen; but, on higher screen sizes, the content is centered on the page with a limited maximum width. If we merely utilized srcset
, as seen above, our photos would scale to the entire size of the browser window, making them larger than necessary on larger screen sizes. This is where the size
attribute comes in.
The sizes
attribute allows you to specify a single size for your image, such as 50vw
, or a list of media queries that will be used to determine the appropriate size for your image. When you do not add the sizes
attribute to your img
, it defaults to a size of 100vw
, which is why the images above scaled off the entire width of the browser window. Let's look at how we may use the sizes
attribute to account for a blog like this, which has a maximum size.
<img
src="house-1200.png"
alt="A house"
srcset="house-400.png 400w, house-800.png 800w, house-1200.png 1200w"
sizes="(max-width: 800px) 100vw, 800px"
/>
The code above is identical to the previous code, but we've added the sizes
attribute. The sizes parameter allows comma-separated lists of media queries and sizes
. To further understand what's going on, let's break down each item on the list.
Our first item (max-width: 800 pixels) 100vw
consists of two sections. The first portion is the media query we wish to compare to. In this scenario, we are checking to see if the screen width is less than 800 pixels. The second section specifies the size of our image if the media query is true. In this situation, we're using 100vw
, which means we want the browser to select an image size depending on the entire width of the browser window.
The second item, 800px
, does not have a media query and instead only has a size. This is our "fallback" size. If all of the media queries defined before this size return false, this fallback size will be used instead. Essentially, it's like having a media query that always returns true. With this item, we are indicating that our image should be selected assuming that it takes up 800 pixels on the screen. The browser will then use this size to select which image to download. If your browser is set to a high resolution or you are zoomed in on the website, it may download an image larger than 800 pixels. However, in general, this is an effective method of ensuring that your images are not larger than necessary.
If we combine these two items, we are effectively saying that our image should be picked based on the browser width up to 800px. At this point, the image will never take up more than 800 pixels on our screen, therefore we should size it accordingly. This is how I would write code to add responsive photos to this blog, as my blog has a maximum width at higher screen sizes. Let us see an example of this in action.
<img
style="width: 100%; border-radius: 1rem;"
src="https://warehouse.co/3200x800/png"
srcset="
https://warehouse.co/400x100/png 400w,
https://warehouse.co/800x200/png 800w,
https://warehouse.co/1200x300/png 1200w,
https://warehouse.co/1600x400/png 1600w,
https://warehouse.co/3200x800/png 3200w
"
sizes="(max-width: 800px) 100vw, 800px"
/>
Potential Pitfalls
The sizes
attribute is extremely powerful, but there are a few things to consider while utilizing it.
Order Matters
Using Percentages
Order Matters
If you have a sizes
attribute with several media queries, the image selected will be the first media query that returns true. This means that the sequence of your media queries is important.
<img
src="https://warehouse.co/3200x800/png"
srcset="
https://warehouse.co/400x100/png 400w,
https://warehouse.co/800x200/png 800w
"
sizes="(max-width: 800px) 100vw, (max-width: 500px) 50vw, 1200px"
/>
If you wrote out your sizes
as shown above, your code would not work as intended. The reason for this is that the initial media query (max-width: 800px)
100vw
will be true for all screen widths smaller than 800px. This means that the second media query (max-width: 500px)
50vw
will never be used because it is only true when the screen is less than 500px, whereas the first media query is always true in those size ranges, therefore it will always be chosen first. To remedy this, organize your media queries so that the most particular searches appear first and the least specific queries appear last.
<img
src="https://warehouse.co/3200x800/png"
srcset="
https://warehouse.co/400x100/png 400w,
https://warehouse.co/800x200/png 800w
"
sizes="(max-width: 500px) 50vw, (max-width: 800px) 100vw, 1200px"
/>
It is also critical to ensure that your default size, the one without a media query, is always last because it always evaluates to true; if it is first, it will always be chosen over any other media query.
Using Percentages
So far, I've shown you how to utilize exact measurements, such as px
, and measurements that scale off the browser window, such as vw
. But what about percentage sizes, such as 50%
? Unfortunately, percentage sizes are not supported by the sizes
attribute. The reason for this is that the browser cannot determine how wide anything defined in percentages will be until it knows the width of the parent element. This implies that the browser will have to wait for the complete page to load before deciding which image to download. This would provide a poor user experience because the visitor would have to wait for the entire website to load before viewing any images.
picture
Element
Up to this point, we have particularly discussed how to render the same image at multiple sizes to help with load speeds, but this does not address the scenario in which you wish to display a different image at different screen sizes. For example, if you had a large header on your page that took up the full width of the page, you might want to display a different image on mobile than you do on desktop because desktop allows you to utilize a more detailed image. This is where the picture
element comes in.
The picture
element allows you to declare numerous source
elements, which are used to define distinct pictures for use on different screen sizes. The browser will then select the first source
element that fits the current screen size and use that image. If none of the source
elements match the current screen size, it will fall back to the img
defined in the picture
element.
<picture>
<source media="(max-width: 500px)" srcset="stairs-narrow.png" />
<img src="stairs-wide.png" alt="Someone coming down the stairs" />
</picture>
When testing this code; If you adjust the size of your browser, the image will shift between the two versions. If you're using a mobile device, you might need to zoom in or out to see the image change to a cropped version of the image stairs-narrow.png
for smaller screen sizes because the image's focal point.
Now, let's look at the code to understand how it works. To make the picture
element work, you must include a regular img
tag at the end.
<picture>
<img src="stairs-wide.png" alt="Someone coming down the stairs" />
</picture>
This will simply render the image as a normal img
tag. Where things gets more complicated is with the source
element. Each modification of your image, aside from the default one, should have its own source
element. In our example, we only had one source
, but you might have as many as you require.
<picture>
<source media="(max-width: 500px)" srcset="stairs-narrow.png" />
<source media="(max-width: 1000px)" srcset="stairs-medium.png" />
<img src="stairs-wide.png" alt="Someone coming down the stairs" />
</picture>
Each of these source
items contains two major attributes. The srcset
attribute functions similarly to the srcset
attribute of the img
tag. This means that if we have several resolutions of the stairs-narrow.png
image, we may include them in the source set. However, when utilizing the picture
element, you will most likely only have one resolution in each source
element, thus you can just specify that as the single url in the srcset
property.
The other attribute is the media
attribute. This is similar to how media queries function with the sizes
attribute, except the source
element media
attribute allows you to define only one media query. These searches are then tested from top to bottom, much like the sizes
attribute, and the first one that matches is used. If none of the media queries match, the img
tag is utilized as a fallback, which is why we don't have a source
element tailored to bigger screen sizes.
Why Choose picture
Element Over Others
One common question about the picture
element is why you would use it instead of the img
element's sizes
attribute or CSS.
Why not sizes
The key reason to utilize the picture
element is that it will always switch to the image defined in the source
element that corresponds to the current screen size. This implies that if you zoom or resize the window, it will switch to the appropriate image.
The sizes
attribute works similarly, but only when the screen size is increased. If your screen size shrinks, the browser will not switch to or download the smaller image because it already has the larger image and will continue to render it. This is wonderful since it saves bandwidth because there's no use in downloading a smaller image when you already have a larger image, but it might be difficult to display different images on different screen sizes, which is why the picture
element is so useful.
Why not vanilla CSS
If you're familiar with CSS, you might see that we can get a very similar outcome by using a few simple CSS attributes.
img {
object-fit: cover;
object-position: center;
}
This will cause the image to cover the complete width of the parent element before cropping it so that the image's center remains visible. This will produce fairly similar results, but the problem is that we will still need to download the full quality version of the image, even on small screen sizes when just a quarter of it is displayed. This contradicts everything we're trying to achieve by employing responsive pictures.
Conclusion
Responsive pictures may appear to be a tedious topic, but they do not have to be. Basic responsive pictures can be implemented by just adding a srcset
attribute to your img
tag and allowing the browser to handle the rest. If you want to be more specific, you can use the sizes
attribute to assist the browser in selecting the appropriate image, or you can use the picture
element to display different images at different screen sizes.