When speaking about responsive images, we usually focus on CSS techniques that make an image adapt to different viewport sizes, such as setting the max-width
property to 100 percent. However, using solely CSS to make images responsive won’t improve performance and page load times, as you’re still serving the same size image for all devices. For instance, loading a 2000px image on mobile comes with a huge (and unnecessary) overhead.
Luckily, HTML also has its own syntax, elements, and attributes for responsive images that let you serve different images for different viewport sizes, resolutions, and other conditions. In this guide, we’ll look into how to add responsive images in HTML and discuss the following features:
<img>
elementsrcset
and sizes
attributesx
and w
descriptors<picture>
and <source>
elementsmedia
and type
attributesTo add responsive images in HTML, your starting point is always the standard image syntax, consisting of the <img>
element and the src
and alt
attributes:
<img src="images/myimage.jpg alt="my image">
For the src
attribute, you can use either an absolute (starting with the http://
or https://
protocol) or a relative path — I’ve used the latter above.
You always need to “build” the responsive image syntax on top of this standard <img>
definition; this also ensures backward compatibility.
srcset
attributeThe srcset
attribute is an optional attribute of image-related HTML elements, including the <img>
tag. You can use it to assign different image sources to certain features of the user’s device, such as viewport size or pixel density. The user’s browser will only load the image that’s the most suitable for the user’s device — which can mean a significant performance gain.
When using the srcset
attribute, you’re supposed to add the same image in different sizes. This is because this attribute only gives hints to the user’s browser that it can still load a different image as it also considers other things, such as the bandwidth of the network. If you use srcset
, the browser will assume that all of the image sources are visually identical. If you want to serve different-looking images, you’ll need to use the <picture>
and <source>
elements — we’ll look into them later on.
There are two ways to serve different size images with the srcset
attribute:
x
descriptors:
w
descriptors and the sizes
attribute:
w
descriptor” stands for “width descriptor”Now, let’s have a look at the respective syntaxes.
srcset
attribute with x
descriptor(s)Using the following HTML, you can serve images for both low-resolution and high-resolution displays:
<img src="images/myimage.jpg" srcset="images/myimage-2x.jpg 2x" alt="my image">
The second image, myimage-2x.jpg
is twice as big as the default one (e.g. 1280x960px compared to 640x480px), but it will only be loaded on high-resolution screens. It will be the user’s browser that decides which image to serve, mostly based on the pixel density of the display.
Note that you are supposed to add the smaller image to the src
attribute because this will be the default. For the srcset
attribute, you also need to use the 2x
descriptor so that the browser will know that this is the image intended for high-resolution screens.
You can add more than one value to the srcset
attribute, too. For instance, with the following code, you can serve images for 4K monitors:
<img src="images/myimage.jpg" srcset="images/myimage-2x.jpg 2x, images/myimage-4x.jpg 4x" alt="my image">
To add more than one image source to srcset
, you need to use the respective x
descriptors (2x
, 3x
, 4x
, etc.) and separate the value pairs by commas.
srcset
attribute with w
descriptor(s) and the sizes
attributeIf you want to target both the pixel density and the layout size of the user’s device, you’ll need to use the srcset
attribute together with one or more width
descriptors and the sizes
attribute.
A w
descriptor defines the width of a source image. For instance, 600w
indicates that the image is 600px wide. As long as you separate them with commas, you can add as many image sources to the srcset
attribute as you want, like so:
<img src="images/myimage-small.jpg" srcset="images/myimage-small.jpg 300w, images/myimage-medium.jpg 600w, images/myimage.jpg 1200w, images/myimage.jpg 1800w" sizes="(max-width: 500px) 100vw, (max-width: 1000px) 90vw, calc(60vw - 20px)" alt="my image">
If you use width
descriptors, you’ll need to use the sizes
attribute, too — otherwise, the browser won’t be able to find out which image source to choose. The sizes
attribute is frequently misunderstood because people tend to assume that the width values used in the media conditions of the sizes
attribute are related to the w
values (300w
, 600w
, etc.) used in the srcset
attribute — however, they are independent of each other.
The sizes
attribute serves two purposes:
w
descriptors) and the pixel density of the screen:
1200w
one, as it does for a larger, low-res screen — let’s say 17.3”. The browser’s choice also depends on the size distribution of the available images and other conditions because, as I’ve mentioned above, the srcset
and sizes
attributes are optional but not obligatory for the browser (as opposed to the <picture>
and <sources>
elements)In the above code example, I’ve defined three layouts within the sizes
attribute. On viewports smaller than 500px, the image will span across 100 percent of the viewport width (100vw
). On medium viewports that are smaller than 1000px, the image will need 90 percent of the viewport width (90vw
). And, on viewports larger than 1000px, the browser will need to allocate 60 percent of the viewport width, minus 20px for the left and right margins (calc(60vw - 20px)
). The latter is also the default value, so I haven’t used any media conditions here.
You can use any number of media conditions to define different layouts and the space the image will need in each, but it is important to note that adding media conditions to the sizes
attribute is optional. It only has to include one default value, and if your layout looks the same at all viewport sizes, you don’t need to use any media conditions. For instance, the above example could alternatively look like this:
<img src="images/myimage-small.jpg" srcset="images/myimage-small.jpg 300w, images/myimage-medium.jpg 600w, images/myimage.jpg 1200w, images/myimage.jpg 1800w" sizes="60vw" alt="my image">
Beware: you’ll find some tutorials saying that you can use the x
and w
descriptors together, but according to the docs, these two shouldn’t be used together. This does make sense when you think about it, as when you use w
descriptors, the browser also considers pixel density, too.
So, if you just want to target pixel density, use x
descriptors. If you want to target both pixel density and layout, use w
descriptors.
<picture>
and <source>
elementsWhile the <img>
element used with the srcset
attribute and x
or w
descriptors is an excellent solution if you want to serve the same image in different sizes, sometimes you’ll want to load visually different images for different user agents.
There are two main use cases that cover why you might want to do that:
HTML has two elements for these situations: <picture>
and <source>
. The latter can be used to specify different media resources for the <picture>
, <audio>
, and <video>
elements.
Now, let’s see what the syntax looks like.
Let’s say you have the following image:
This is a perfect image for large screens, but for medium-sized screens you might want to serve a closer shot that’s also smaller in size:
And on small screens, you just want to show one of the flamingos in a much smaller image:
You can define these three image sources using the following HTML code:
<picture> <source srcset="images/flamingos-closer.jpg" media="(min-width: 768px) and (max-width: 1199px)"> <source srcset="images/flamingos-far.jpg" media="(min-width: 1200px)"> <img src="images/flamingo-profile-small.jpg" alt="flamingo"> </picture>
As you can see above, we added the smallest, closest image as the default and defined a relevant media condition for each image source.
If you want, you can also specify more than one image within each srcset
attribute using the aforementioned x
or w
descriptors, for example:
<picture> <source srcset="images/flamingos-closer.jpg, images/flamingos-closer-2x.jpg 2x" media="(min-width: 768px) and (max-width: 1199px)"> <source srcset="images/flamingos-far.jpg, images/flamingos-far-2x.jpg 2x" media="(min-width: 1200px)"> <img src="images/flamingo-profile-small.jpg" srcset="images/flamingo-profile-small-2x.jpg 2x" alt="flamingo"> </picture>
Note that with the media
attribute, you can target both width and any other media features, such as orientation, aspect ratio, and more.
If you use the <picture>
element with the <source>
tag, the user’s browser will still load only one image — the most suitable one — but now, the media condition is not a hint or an option like before. Instead, it’s a rule that the browser has to follow in any case. In other words, the browser will assume that the image sources are visually different from each other and treat them accordingly.
Besides media conditions, you can also serve an image in different formats. This can be especially useful if you want to use next-generation image formats, such as AVIF or WebP, that are smaller in size (which can improve performance on their own) but are not supported by older browsers.
Here, you’ll need to define the MIME type of each image source using the type
attribute:
<picture> <source srcset="images/myimage.avif" type="image/avif"> <source srcset="images/myimage.webp" type="image/webp"> <img src="images/myimage.jpg" alt="my image"> </picture>
If you use the code above, the user’s browser will check the consecutive MIME types one by one — so, add the one that you want to have checked first (I used AVIF in the example above).
Technically, you could also use the type
and media
attributes together within each <source>
element — however, note that this can add a lot of extra complexity:
<picture> <source srcset="images/flamingos-closer.webp" media="(min-width: 768px) and (max-width: 1199px)" type="image/webp"> <source srcset="images/flamingos-closer.jpg" media="(min-width: 768px) and (max-width: 1199px)" type="image/jpeg"> <source srcset="images/flamingos-far.webp" media="(min-width: 1200px)" type="image/webp"> <source srcset="images/flamingos-far.jpg" media="(min-width: 1200px)" type="image/jpeg"> <source srcset="images/flamingo-profile-small.webp" type="image/webp"> <img src="images/flamingo-profile-small.jpg" alt="flamingo"> </picture>
Browser support for the HTML syntax related to responsive images is relatively good:
srcset
and sizes
attributes are supported by Edge 16+, Firefox 38+, Chrome 38+, and Safari 9+, but they’re not supported by any version of Internet Explorer<picture>
element is supported by Edge 13+, Firefox 38+, Chrome 38+, and Safari 9.1+, but it’s not supported by any version of Internet Explorer<source>
element is supported by Internet Explorer 9+, Edge 12+, Firefox 15+, Chrome 90+, and Safari 14.1+As Chrome and Safari began to support the <source>
element relatively late, it comes with the lowest level of browser support out of these four HTML features related to responsive images.
Plus, the responsive image syntax degrades gracefully. If a browser doesn’t support one or more elements or attributes, it will simply use the default image added to the very well-supported <img>
element.
Adding different image sources in HTML to serve the most performant image for each user agent can result in a huge performance gain, especially on mobile devices. However, a complex syntax can make your coding workflow overly complicated and your code harder to read.
Essentially, the trade-off is between complexity and performance, so you need to decide whether it’s worth the hassle for you or not. Obviously, image performance is more important on image-heavy websites, and may not be as important if you have just one or two images on the screen at any given time.
You can also automate the process. CDNs such as Netlify and Cloudflare and CMSs such as WordPress already come with built-in image optimization capabilities — they generate several versions of the same image, add the <picture>
element, apply media conditions, and more. There are also open-source solutions that can help you with automation, such as the Image Responsiver or Get Sizes tools.
Finally, don’t forget that this guide only discusses how to serve responsive images in HTML. You’ll still need to use CSS to adapt the image added by HTML to the design, such as adjusting the width
and max-width
attributes or using a responsive image grid.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowIt’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.