The specifications for responsive images went through years of debate and many iterations + it's a pretty wide area to cover so the syntax is a bit over the place. In this article, I'll try to give you a full understanding of all the different methods for creating responsive images.
I'll break it down into 3 parts:
Why
As discussed in the first part of this series, big images can make your site incredibly slow. We already cut down the file size by compressing our images, but on phones, you still get the same image resolution as on desktop. Responsive images allow you to serve different resolutions depending on either screen size, screen resolution or image width.
Syntax
There is a multitude of ways to make your image responsive, to understand how they work we have to break down a bit of new syntax:
Let's start with an example and go over what all the pieces of syntax do.
<img src="file-200px.jpg"
alt="some file"
srcset="file-200px.jpg 1x,
file-400px.jpg 2x,
file-600px.jpg 3x>
src
src="file-200px.jpg"
This determines the source of the image. It's also used as the source on all browsers that don't support srcset.
srcset
srcset="file-200px.jpg 1x,
file-400px.jpg 2x,
file-600px.jpg 3x
Srcset
is made up of one or more image candidates. The candidates are separated by a comma and each candidate contains an image URL and an optional condition. If the condition is fulfilled the candidate source will be used instead of the default source.
the conditions
1x, 2x etc (display density)
With the introduction of high density screens CSS had to make a distinction between a pixel as a measurement unit and an actual pixel on a screen so that the layout wouldn't look tiny on high-resolution screens.
On the iPhone 11, for example, every CSS pixel is actually 2 device pixels big. This means the device pixel ratio is 2x.
srcset="file-200px.jpg 1x,
file-400px.jpg 2x,
file-600px.jpg 3x"
So what our example does is it serves users with a high-density screen a higher resolution image so that they can take full advantage of their high resolution. At the same time, we make sure that users with a lower density screen don't have to waste unnecessary data.
Translated into words the syntax above means
- if the user has a device pixel ratio of 1x, serve him
file-200px.jpg
- if the user has a device pixel ratio of 2x, serve him
file-400px.jpg
- etc
200w, 400w etc (width descriptors)
srcset="file-200px.jpg 200w,
file-400px.jpg 400w,
file-600px.jpg 600w"
The width descriptor describes the resolution of your file. So the actual width in pixels when you open your image in a photo editor or so. The browser will use that width to decide what image source to use. Unfortunately, just the image resolution isn't enough information for the browser to make a decision.
When the browser begins downloading its assets it doesn't know the width of the slot that the image element will eventually fill in the page, so to use width descriptors we have to tell the browser what size our image will have! Width descriptors can't be used without the sizes attribute, so the whole syntax actually looks like this:
srcset="file-200px.jpg 200w,
file-400px.jpg 400w,
file-600px.jpg 600w"
sizes="(min-width: 900px) 700px,
(min-width: 400px) 80vw,
100vw"
This syntax can be intimidating at first but is actually fairly straight forward, all we do by specifying the sizes is tell the browser:
- "when the screen is more than 900px wide, the width of the slot the image will fill is 700px.
- "when the screen is more than 400px wide the width of the slot the image will fill is 80vw.
- "when none of the media conditions are true the width of the slot the image will fill is 100vw"
You can use absolute units (like px or em) or a length relative to the viewport (vw) but not percentages.
The browser is going to use these sizes to:
- Look at its device width
- Work out which media condition in the sizes list is the first one to be true
- Look at the slot size given to that media query
- Load the image referenced in the srcset list that most closely matches the chosen slot size
The picture tag
The syntax above can be really hard to keep track off if not properly formatted and is still a bit limited. Luckily there's an alternative.
<picture>
<source srcset="example-200px.jpg" media="(min-width: 800px)">
<source srcset="example.webp" type="image/webp">
<img src="example.jpg" />
</picture>
The picture tag takes in one img
element and zero or more source
tags. A good way to think about it is that the picture
element supercharges the img
element in it with the different sources
. Browsers that don't support the picture
or source
tag will simply ignore it and just render the img
element in it.
The source tag
You may have already seen the source tag inside of a video
tag or so. Inside of the picture
tag it can be used to accomplish a variety of things:
use modern image formats
In the example above we use <source srcset="example.webp" type="image/webp">
. Webp is a modern image format that I talked about in the first part of this series. Since browser support for it is still a bit spotty we can't use webp as our only src for an image but with the source
tag we can specify a webp
source and all browsers that support webp will use it. Be sure to specify the type
as image/webp
so that the browser can read it.
change the src depending on viewport size
<picture>
<source media="(max-width: 800px)" srcset="example-small.jpg">
<source media="(max-width: 1200px)" srcset="example-large.jpg">
<img src="example-default.jpg">
</picture>
This syntax is pretty self-explanatory when the viewport size is smaller than 800px it will display a smaller version of the image. Instead of supplying a lower resolution version of your image you can also supply a completely different image on smaller screens. This can be really useful if you want to show for example a less detailed, or more zoomed-in version of an image on smaller screens.
use srcset and size
<source srcset="file-200px.jpg 200w,
file-400px.jpg 400w,
file-600px.jpg 600w">
a source can also support a srcset and sizes tag, just like the regular img
tag.
How
Your approach should always depend on your use case. I would always use a picture tag since it lets you use webp and is the most flexible. When working with a framework like vue, react, svelte etc I recommend making a reusable component that integrates with your backend setup. For example in react it could be something like this:
<MyPictureComponent
sizes={{
default: allSources.medium,
mobile: allSources.small,
desktop: allSources.large
}}
/>
which would render sth like this:
<picture>
<source media="(max-width: 800px)" srcset={sizes.small.webp} type="image/webp" />
<source media="(max-width: 1200px)" srcset={sizes.large.webp} type="image/webp" />
<source media="(max-width: 800px)" srcset={sizes.small.jpg} />
<source media="(max-width: 1200px)" srcset={sizes.large.jpg} />
<img src={sizes.default.jpg} />
</picture>
That way webp is handled by default and the whole thing is a bit less verbose.
Do you have a nice abstraction for responsive images? Let me know 👇
summary
- the srcset attribute allows you to supercharge your img tag by providing it with a list of alternative sources and conditions.
- conditions can be based on the screen density and image size.
- the picture tag allows you to specify a bunch of sources for an image to specify different images at different screen sizes and use modern image formats with a fallback.
Top comments (0)