A comprehensive guide to responsive Images (picture, srcset, source etc)
Tags: html, webdev, css
Published: 2019-11-18 14:57:37

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 you should use responsive images](#why)
 - [A syntax breakdown](#syntax)
 - [My recommended setup for responsive images](#how)

##Why
As discussed in the [first part of this series](https://dev.to/jsco/understanding-image-formats-on-the-web-4op8), 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:
 - [src](#src)
 - [srcset](#srcset)
 - [picture](#the-picture-tag)
 - [source](#the-source-tag)

Let's start with an example and go over what all the pieces of syntax do.

```html
<img src="file-200px.jpg"
     alt="some file"
     srcset="file-200px.jpg 1x, 
             file-400px.jpg 2x, 
             file-600px.jpg 3x>
```

####src

```html
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

```html
     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.

![srcset syntax breakdown](https://thepracticaldev.s3.amazonaws.com/i/jrl30ux9222p3ba40apm.png)

####the conditions


##### 1x, 2x etc (display density)

With the introduction of [high density screens](https://medium.com/@elad/understanding-the-difference-between-css-resolution-and-device-resolution-28acae23da0b) 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**. 

```html
     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)
```html
     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:

 ```html
     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:

1. Look at its device width
2. Work out which media condition in the sizes list is the first one to be true
3. Look at the slot size given to that media query
4. 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. 

```html
<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](https://sv.fyi/image-formats#webp). 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
```html
<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
```html
  <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:

```jsx
<MyPictureComponent
     sizes={{
         default: allSources.medium,
         mobile: allSources.small,
         desktop: allSources.large
     }} 
/>
```

which would render sth like this:

```jsx
<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.