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" />
placeholder image

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"/>
placeholder image

<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"/>

placeholder image

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"/>

placeholder image

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">

placeholder image

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 and width are specified, the sizes is used to select which image to download from srcset, but the width 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">

placeholder image

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 than 300px. The browser looks in the srcset for the most appropriate image, sees the 300w 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, the 300w 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>
placeholder image

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>
placeholder image

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>
placeholder image

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>
placeholder image

<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>
placeholder image

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>
placeholder image

<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>
placeholder image
A placeholder image

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:

  1. Start with the <img>:

    <img src="https://placehold.co/300x100/blue/fff" />
  2. 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">
  3. 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">
  4. 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>
  5. 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>

References