HTML Images
In this post, I am going to work through HTML images from first principals. I will work up from <img>
to srcset
, sizes
, <picture>
, <source>
, <figure>
and <figcaption>
.
The article is mostly written for myself, in order to set a few things straight in my head, you will probably find better explanations elsewhere on the web. I have written this mainly with the intent to get the browser to select the most efficiently sized image to fill the "slot" on the page that it should be placed in. As such I have not talked about other image attributes, notably the alt
attribute. All of the below also assume a device pixel ratio of 1. I might do a follow up post exploring the DPR mechanism.
<img>
Let's start off nice and simple.
<img src="https://placehold.co/600x400/blue/fff" />
The image is shown at "natural resolution" (which is 600px by 400px) as we did not specify anything further for the browser.
We should specify the width and height (in pixels) that we want the image to take up in the document. This is to avoid the document content moving around after the image has been downloaded:
<img src="https://placehold.co/600x400/blue/fff" width="300" height="200"/>
<img>
with srcset
The next step is to add a srcset
attribute to the <img>
tag which allows us to swap images based on the browser width.
<img
srcset="https://placehold.co/600x100/yellow/000 600w, https://placehold.co/600x100/green/fff 800w"
width="600"
src="https://placehold.co/600x100/red/fff"/>
This will show a yellow image when the browser width is 600px and below, and green otherwise. The red image in the src
attribute is there as a fallback incase the browser doesn't understand the srcset
attribute.
I have forced the width of the image to 600px (with width="600"
) so that we can observe the browser swapping in and out the source, without the image itself re-sizing.
If we don't specify the width
attribute when using srcset
, there is a strange behaviour which might not be expected:
<img src="https://placehold.co/600x100/yellow/000"/>
- Shown at "natural resolution"
<img srcset="https://placehold.co/600x100/yellow/000" src="https://placehold.co/600x100/red/fff"/>
- Shown at "natural resolution"
<img srcset="https://placehold.co/600x100/yellow/000 600w" src="https://placehold.co/600x100/red/fff"/>
- Scaled to 100% browser width:
By adding the 600w
into the srcset
, the browser suddenly scales the image to fill the container.
<img>
with srcset
and sizes
Adding a sizes
attribute in the mix allows us to specify to the browser, what width the image should be shown at different browser widths.
Let's go back to just a single image in srcset
again, telling the browser that it is 600px wide (with the width description 600w
).
<img
srcset="https://placehold.co/600x100/purple/fff 600w"
sizes="(max-width: 600px) 300px, 500px"
src="https://placehold.co/600x400/red/fff"/>
THe important part is (max-width: 600px) 300px, 500px
which tells the browser: If the viewport width is 600px or less, the image should be displayed at 300px wide. Otherwise, it should be displayed at 500px wide.
And by adding additional items back to the srcset
, we can change the image shown as well as the width it is shown at based on the browser width:
<img
srcset="https://placehold.co/300x100/pink/fff 300w, https://placehold.co/600x100/purple/fff 600w"
sizes="(max-width: 600px) 300px, 600px"
src="https://placehold.co/600x400/red/fff">
This means, for browser widths up to 600px, show the pink image at 300px width, and for browser widths over that, swap out to the purple image and show it at 600px wide.
❗ NOTE
If both
sizes
andwidth
are specified, thesizes
is used to select which image to download fromsrcset
, but thewidth
attribute is what wins in terms of the size to display the image at to the user.
The sizes attribute can also be used with the view width vw
unit which allows for specifying image widths relative to the browser width:
<img
srcset="https://placehold.co/300x100/pink/fff 300w, https://placehold.co/600x100/purple/fff 600w"
sizes="(max-width: 300px) 100vw, 50vw"
src="https://placehold.co/600x400/red/fff">
This has an interesting effect as the image now shows 3 different things based on the browser width:
- Browser width 0px - 300px: Show the pink image at 100vw. This is because the
(max-width: 300px) 100vw
part tells it to stretch the image to fit the view port, which will be less than300px
. The browser looks in thesrcset
for the most appropriate image, sees the300w
and selects that so the pink image is shown. - Browser width 301px - 600px: Show the pink image at 50vw. Between 301px and 600px browser widths, the
sizes
attribute tells the browser to show the image at half browser width, or 150px to 300px. At all of those widths, the300w
image is the most appropriate and so the pink image is shown again. - Browser width 601px+: Show the purple image at 50vw. After the browser has been scaled beyond 600px, the 50vw means that the image size will be larger than 300px, and so the browser selected the
600w
image and scales it down to fit.
<picture>
HTML also allows us to specify a <picture>
element as a parent to the <img>
tag.
<picture>
<img src="https://placehold.co/600x400" />
</picture>
Which doesn't do much in itself; the <img>
tag is simply used by the browser as it was before. The interesting bit happens when you start to add <source>
elements in:
<source>
with type
If we want to simply provide the same image as a different file format, we can introduce a <source>
element with type=""
attribute. This allows the browser to choose which format of image it wants to show to the user. The <img>
tag is still used as a fallback if the browser doesn't support the <picture>
element, or if none of the type=""
element match with what it can show.
<picture>
<source srcset="https://placehold.co/600x400.webp?text=webp" type="image/webp" />
<img src="https://placehold.co/600x400.jpg?text=jpg" />
</picture>

If your browser supports showing webp
images, it will download the first URL and show that. If not, it will fallback to the jpg.
Of note, is that if you apply a width attribute to the <img>
tag, it will apply even if the src gets swapped in for a different srcset
.
<picture>
<source srcset="https://placehold.co/600x400.webp?text=webp" type="image/webp" />
<img src="https://placehold.co/600x400.jpg?text=jpg" width="100"/>
</picture>

We can also add in the sizes
attribute from earlier to select different images at different browser widths:
<picture>
<source srcset="https://placehold.co/300x200.webp?text=webp-300px 300w, https://placehold.co/600x200.webp?text=webp-600px 600w"
sizes="(max-width: 300px) 100vw, 50vw"
type="image/webp" />
<img src="https://placehold.co/600x200.jpg?text=jpg"/>
</picture>

<source>
with media
Instead of using sizes
to change the image that is shown at different browser widths, we can also using the media
attribute to switch out the source:
<picture>
<source srcset="https://placehold.co/600x100/green/fff" media="(max-width: 600px)" />
<source srcset="https://placehold.co/600x100/yellow/000" media="(min-width: 601px)" />
<img src="https://placehold.co/600x400?text=Fallback"/>
</picture>
Which will show the green image up until the browser width surpasses 600px, and then it will swap in the yellow image. This has the exact same effect as this image tag:
<img src="https://placehold.co/600x400?text=Fallback"
srcset="https://placehold.co/600x100/green/fff 300w, https://placehold.co/600x100/yellow/000 600w"
sizes="(max-width: 600px) 300px, 600px"/>
<figure>
We can wrap the image in a <figure>
tag, which doesn't do much in itself:
<figure>
<img src="https://placehold.co/300x200" />
</figure>
<figure>
with <figcaption>
Now we can also add a <figcaption>
element which allows us to describe the figure alongside the image:
<figure>
<img src="https://placehold.co/300x200" />
<figcaption>A placeholder image</figcaption>
</figure>
Constructing an image tag
When constructing a proper image tag, I like to think of it in waves, iterating over each previous output to add a new consideration:
-
Start with the
<img>
:<img src="https://placehold.co/300x100/blue/fff" />
-
What size do you want it appear on the page? (Example requirement: at 300px and below, the image must cover the viewport width. Above that it must be half the view port width)
<img
srcset="https://placehold.co/300x100/green/fff 300w"
sizes="(max-width: 300px) 100vw, 50vw"
src="https://placehold.co/600x100/blue/fff"> -
What different widths of images do you want displayed? (Example requirement: show the green image when the image "slot" is 300px or smaller, and the purple image when it is above that)
<img
srcset="https://placehold.co/300x100/green/fff 300w, https://placehold.co/600x100/purple/fff 600w"
sizes="(max-width: 300px) 100vw, 50vw"
src="https://placehold.co/600x200/blue/fff"> -
What different image formats do you want to support? (Example requirement: I want to show webp images, and fallback to jpg)
<picture>
<source srcset="https://placehold.co/300x100/green/fff.webp 300w, https://placehold.co/600x100/purple/fff.webp 600w"
sizes="(max-width: 300px) 100vw, 50vw"
type="image/webp" />
<img src="https://placehold.co/600x200/blue/fff.jpg?text=jpg"/>
</picture> -
Do you want to add a figure caption? (Example requirement: add a figure caption)
<figure>
<picture>
<source srcset="https://placehold.co/300x100/green/fff.webp 300w, https://placehold.co/600x100/purple/fff.webp 600w"
sizes="(max-width: 300px) 100vw, 50vw"
type="image/webp" />
<img src="https://placehold.co/600x200/blue/fff.jpg?text=jpg"/>
</picture>
<figcaption>Placeholder image</figcaption>
</figure>