srcset, sizes, picture, media: What you might not have known.
Tags: responsive, frontend, html, design
Published: 2020-09-29 12:04:11

Gosh. Yet another "How-To Responsive Images" article? Well nope, I hope.

![](https://media.giphy.com/media/duLDZKWk8WAaSIkmDv/giphy.gif)

## The background story: It all started with an `<img>` tag
So one of my colleagues was going like 

> WTF Dude, Firefox is loading some bunch of huge images even though I provided a proper srcset and sizes attribute. 

In 20 years of programming experience - also in these modern times - me and the team have seen some weird browser bugs here and there so I do not immediately assume the problem is the one in front of the computer.

However in this case, for something so crucial I and *rather* simple I couldn't believe it's a Browser problem. Especially as Safari was behaving the same way.


It was something like this:

```html
<img src="raw.jpg"  
srcset="360w.jpg 360w, 800w.jpg 800w, 1080w.jpg 1080w" 
sizes="
  (max-width: 360px) 360px,
  (max-width: 800px) 800px,
  (max-width: 1080px) 1080px
">
```

For a Samsung Galaxy S9 with `360x740` it would load the `1080px` one. And we both were a bit confused.

I don't want to create cliffhangers so I will immediately jump to the problem: The S9 has a `4x` density as its *actual* resolution is way more than the above. It is `1440x2960 (h x w)`. So yeah, divide that by 4 and you will get `360x740`.

Now what the browser did was quite clever:

> Yo Dog, I know you wanted `360w` image on `360px` screen but this equals 100% width on this mobile phone which is actually `1440px` to have a sharp view so I will choose the closest that fits that image size which is `1080px`

### It is not directly "intuitive" but it makes sense

It makes sense when you think about it. But for that you have to accept the fact that an `img` tag with `srcset` is something where you give the browser options (we will have a closer look on this). You are not necessarily the *Director* of the scene. If the browser thinks that a certain image out of the provided ones in `srcset` is the best fit according to the given information then it will choose that image.

### So what was the solution now?

We now know what happened and why it happened and I love to tell you the solution - but it really depends on what you want to achieve.

In this specific case he wanted to deliver a specific image depending on the width of the device. So what he could've done (not saying should've done) to deliver the image that he actually wanted is the following:


**Before:**
```html
<img src="raw.jpg"  
srcset="360w.jpg 360w, 800w.jpg 800w, 1080w.jpg 1080w" 
sizes="
  (max-width: 360px) 360px,
  (max-width: 800px) 800px,
  (max-width: 1080px) 1080px
">
```

**After:**
```html
<img src="raw.jpg"  
srcset="360w.jpg 360w, 800w.jpg 800w, 1080w.jpg 1080w" 
sizes="
  ((max-width: 360px) and (-webkit-min-device-pixel-ratio: 4)) 90px,
  ...
  (max-width: 800px) 800px,
  (max-width: 1080px) 1080px
">
```

So `360/4 = 90px` so when the Browser will search for the best image fit now it will go "Okay I need a 4 times bigger image than `90px` which is `360px` so I am taking the 360w.jpg"

**That does not sound like a clean solution does it?** Especially when you think about the fact that you would also intentionally deliver an image with non-fitting quality (4 times less the quality to be exact).
However the next time a 5x or 6x DPI device hits the market you need to adapt more and more in your queries of the `sizes` attribute.

### David you talked a lot about the issues but what would you recommend?

I recommend: *Start with thinking what you want to see as an end-result*. Maybe even draw it on paper. Then continue to think which solution will help you to achieve that and it might not be an `img` tag with `srcset` but maybe the `picture` tag instead. Please read the explanations below for that.

**If its the same image on all devices no matter what but with different sizes you should go for using *Retina* definitions in your `srcset`**:

```html
<img src="raw.jpg"  
srcset="
  image.jpg,
  image@2x.jpx 2x,
  image@3x.jpx 3x,
  image@4x.jpx 4x,
  image@5x.jpx 5x">
```

---

# It is all about understanding the specs

## `img`: The important specification details on `srcset` + `sizes`

If you use `srcset="foo.jpg 200w ..."` then you **MUST**  (check the [W3C specs](https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset)) provide a `sizes` attribute to tell the browser "On this media query the image will have a shown size of WHATEVERpx". 

If you use `srcset="foo.jpg 2x ..."` you **MUST NOT** provide a `sizes` attribute. The browser would just ignore it as it implies the chosen image only by the pixel density.

According to https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element (09/2020) the `img` tag does not support the attributes `media`, `type`. Which is understandable if you read the following paragraphs about `picture` and its `source` tags.

## The powerful `picture` tag and its `source` children
 
Semantically, (reading the W3C specs) the `picture` element is just a "ghost" container for the `img` tag that allows to control what image is served in the `img` tag.

[W3C states](https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element):
> The picture element is a container which provides multiple sources to its contained img element to allow authors to declaratively control or give hints to the user agent about which image resource to use, based on the screen pixel density, viewport size, image format, and other factors. It represents its children.

[MDN describes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction) the `picture` tag as the tag for the goal of *Art Direction*. What they mean is: Total Flexibility. The big difference to `img[srcset][sizes]` is that you do not let the browser choose which is the best fit for the device size. You explicitly define what image shall be played under what exact circumstances (media query). This can be based on pixel density, screen ratio, height, whatever you want.

The picture element must at least contain an `img` tag and for it to be actually useful it should contain 1 or more `source` tags.

Let us go by examples.

### 1. Retina Image with `picture` tag
We simply want to scale up the images depending on device pixel density.

```html
<picture>
 <source srcset="logo.png 1x, logo_x2.png 2x, logo_x3.png 3x">
 <img src="logo.png" alt="Cool Logo 👌">
</picture>
```

This sample could be seen as over-engineered since removing the `picture` and `source` tag but keeping the `img` tag would do the same thing:

```html
<img src="logo.png" alt="Cool Logo 👌" 
  srcset="logo.png 1x, logo_x2.png 2x, logo_x3.png 3x">
```

### 2. Choosing a different image depending on ratio

```html
<picture>
 <source 
  media="(min-aspect-ratio: 21/9)"
  srcset="2100x900px-img.jpg">
<source 
  media="(min-aspect-ratio: 16/9)" 
  srcset="1600x900px-img.jpg">
<source 
  media="(min-aspect-ratio: 1)" 
  srcset="300x300px-img.jpg">
 <img src="fallback_whatever.png" alt="💞">
</picture>
```

The above will solely take the aspect ratio of the screen into account. So for a `600x600` screen with actual resolution of `3000x3000` which would be `5x` retina it would still use the `300x300px-img.jpg` because that is the first media query that matches.

> This is important to note: `source` tags are matched from top to bottom. If one hits, the following are ignored.

### 2. Choosing a different image depending on ratio whilst still providing proper retina images

```html
<picture>
 <source 
  media="(min-aspect-ratio: 21/9)"
  srcset="
   2100x900px-img.jpg 1x, 
   4200x1800px-img.jpg 2x,
   6300x2700px-img.jpg 3x">
<source 
  media="(min-aspect-ratio: 16/9)" 
  srcset="
   1600x900px-img.jpg 1x,
   3200x1800px-img.jpg 2x,
   4800x2700px-img.jpg 3x,">
<source 
  media="(min-aspect-ratio: 1)" 
  srcset="
   300x300px-img.jpg 1x,
   600x600px-img.jpg 2x,
   900x900px-img.jpg 3x">
 <img src="fallback_whatever.png" alt="💞">
</picture>
```

The only difference to our previous sample now is that we tell the browser exactly which `source` tag to take but then the browser is able to choose the best image **within** this source tag. So if its `8x` retina but a squared screen it would hit the last `source` tag and choose the next best fit which is `3x` which is `900x900px-img.jpg`.

### 3. Improving performance with `source` tag and `type` attribute

Let us take a simple sample and tune it only for the sake of performance with the `type` attribute.

Before tuning:
```html
<picture>
 <source 
  media="(min-width: 1024px)"
  srcset="
   big.jpg 1x, 
   big-2x.jpg 2x">
<source 
  media="(min-width: 768px)"
  srcset="
   medium.jpg 1x, 
   medium-2x.jpg 2x">
 <img src="fallback.jpg" 
  srcset="
   fallback.jpg 1x, 
   fallback-2x.jpg 2x" alt="💞">
</picture>
```

As you can see we have 3 potential sources. It is totally okay to also use `srcset` on the `img` within a `picture` tag as it will only be considered when the other 2 `source` tags are not matched.

The [W3C says](https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-srcset):
> The srcset attribute and the src attribute (if width descriptors are not used) contribute the image sources to the source set (if no source element was selected).

Let's tune:
```html
<picture>
 <source 
  media="(min-width: 1024px)"
  type="image/webp"
  srcset="
   big-optimized.webp 1x, 
   big-optimized-2x.webp 2x">
 <source 
  media="(min-width: 1024px)"
  srcset="
   big.jpg 1x, 
   big-2x.jpg 2x">
 <source 
  media="(min-width: 768px)"
  type="image/webp"
  srcset="
   medium-optimized.webp 1x, 
   medium-optimized-2x.webp 2x">
 <source 
  media="(min-width: 768px)"
  srcset="
   medium.jpg 1x, 
   medium-2x.jpg 2x">
 <source 
  type="image/webp"
  srcset="
   fallback-optimized.webp 1x, 
   fallback-optimized-2x.webp 2x">
 <img src="fallback.jpg" 
  srcset="
   fallback.jpg 1x, 
   fallback-2x.jpg 2x" alt="💞">
</picture>
```

Now we not only provided different images for different resolutions but we also provided the browser the possibility to then take the retina image and of that even the optimized version in the `webp` format instead of `jpg`.

![](https://media.giphy.com/media/cL4pqu8GGRIihabgSM/giphy.gif)


### `picture` tag Conclusion
The `picture` tag allows you to play a different image in different situations. E.g. if you were going to play a moving `.gif` file but you want to improve A11y you could do something like:

```html
<picture>
 <source 
  media="(prefers-reduced-motion: reduce)"
  srcset="
   no-motion.jpg 1x, 
   no-motion2x.jpg 2x,
   no-motion3x.jpg 1x 3x">
 <img src="funny_moving_gif.gif" alt="Funny Animals">
</picture>
```

---

# Conclusion: So when to use what?

## `picture`
If you have a complex setup do yourself a favor, use `picture`.

## `<img srcset sizes ...>`
If you have an image that stays the same ratio but shall be played in different quality versions e.g. actual Photographs such as those uploaded on Instagram or Facebook then you might would want to simplify your life by using `<img srcset>` or `<img srcset sizes>` depending on what you want to achieve.

Use the comments below for any questions.