Yes, I’m guilty! I’m guilty of ignoring the web platform and all the extraordinary riches waiting for discovery. For all the lip service the .NET community loves to give towards being performance-minded, it’s a shame we stop at the response leaving the server. No more fellow reader; it’s time we think beyond the server and think about HTML and, in the case of this post, images.

In this post, I’ll show you how to optimize your image delivery using newer HTML features and the differences between the two approaches. We’ll see how and why you may want to use each approach by weighing the pros and cons. Finally, we’ll look at cute puppies because who doesn’t love puppies?! Nobody.

The Humble Image Tag

If you’ve ever written HTML, you’ll almost certainly have used the img tag. Images are an essential element of most web pages, yet they can also be the downfall of many. Suboptimal images can drag down your Core Web Vitals, the metrics used to determine how a user may perceive your site’s performance. Let’s look at a plain old image tag you may typically see in an HTML response.

<img alt="good boy corgi" src="/img/good-boy@3x.webp">

Nothing outrageous, right? Well, this particular image is sub-optimal for many reasons.

  • This image is 3.1 MB and has dimensions of 2000px x 2952px
  • The image tag lacks width and height attributes, so loading the image will cause layout shifts and page redraws
  • We serve one image for all devices, regardless of pixel density or medium. It’s wasteful.

Let’s see if we can do better.

Descriptive Responsive Image

The img tag has a few new attributes that can help developers address the above-mentioned issues. Those attributes are srcset and sizes.

The srcset attribute allows you to define a set of source images the browser should consider when rendering the HTML to the user. The critical word here is “should”, as the browser determines the selected image. We’ll see how to steer the browser in a particular direction, but it will always be up to the browser. First, let’s see an example of an image with a srcset.

<img
    srcset="/img/good-boy@3x.webp 2000w, /img/good-boy@2x.webp 1000w, /img/good-boy@1x.webp 500w"
    src="/img/good-boy@1x.webp"
    alt="a good boy - corgi puppy"
    decoding="async"
    >

Three images are defined with their width descriptor specified in <number>w format. The width descriptor tells the browser what the image’s intrinsic width is in a unit-agnostic way. This is important as some displays have pixel densities of 1x, 2x, or 3x. We also set the src tag as a fallback for clients that do not support srcset, although those clients are almost non-existent.

Let’s give the browser hints on what to choose when displaying one of our three options. That’s where the sizes attribute comes into play.

<img  
    srcset="/img/good-boy@3x.webp 2000w, /img/good-boy@2x.webp 1000w, /img/good-boy@1x.webp 500w"  
    sizes="(max-width:500px) 500px, (max-width:1000px) 1000px, (max-width:2000px) 2000px"  
    src="/img/good-boy@1x.webp"  
    alt="a good boy - corgi puppy"  
    decoding="async"  
    >

Let’s see what the sizes mean:

  • When the maximum width of the viewport is 500px, the image will take up 500px in layout.
  • When the maximum width of the viewport is 1000px, the image will take up 1000px in layout.
  • When the maximum width of the viewport is 2000px, the image will take up 2000px in layout.

We use CSS to define matches in the form of (max-width:500px). Additionally, we could use the use case and assume all images will always be displayed at 100vw.

<img
    srcset="/img/good-boy@3x.webp 2000w, /img/good-boy@2x.webp 1000w, /img/good-boy@1x.webp 500w"
    sizes="100vw"
    src="/img/good-boy@1x.webp"
    alt="a good boy - corgi puppy"
    decoding="async"
    >

Again, it’s important to note that the client ultimately decides which option is best given the current state of the viewport.

What are the advantages of this approach?

  1. The client knows best, so let it choose for you.
  2. Once the largest-size asset is loaded, it will always be used from the cache. Upgrades never any downgrades in quality.
  3. Less HTML than the other option.

What are the disadvantages?

  1. Lack of control and “art direction”
  2. Black box of when and why things might be chosen.

Let’s look at the next option, the picture tag, and see how we may overcome some disadvantages.

The Descriptive Approach to Responsive Images

The picture tag allows us to describe what image to serve and when to serve it. This allows us a level of control not available to folks using the img tag with srcset and sizes. Let’s take a look at an example.

<picture>
    <source media="(max-width: 500px)" width="500" height="738" srcset="/img/sad-boy@1x.webp">
    <source media="(max-width: 1000px)" width="1000" height="1476" srcset="/img/sad-boy@2x.webp">
    <source media="(min-width: 2000px)" width="2000" height="2952" srcset="/img/sad-boy@3x.webp">
    <img src="/img/sad-boy@3x.webp" alt="a sad boy - French bulldog in a hoodie" loading="lazy">
</picture>

As you may notice, it’s more verbose, but we now can choose precisely what image gets displayed and when it gets displayed.

This can give us a sense of art direction not available with the previous method. We could display images optimized for mobile devices or desktops based on media queries. This can help us optimize the size and dimensions of an image like we couldn’t before.

Advantages of this approach?

  • Art Direction - Images change as the viewport changes
  • Flexibility
  • Still delivering the optimal experience.

Disadvantages to this approach?

  • Verbose - lots and lots of HTML
  • Resizes may cause additional network requests for new assets

Let’s see both of these examples in a demo.

Responsive Images Demo Time

This video is hosted on Mastodon, so you can head to my Mastodon page to see it in action. You can also head to my GitHub repository to download the sample ASP.NET Core project.

As you notice, when I resize the window, the pictures of puppies change (look at the 1x, 2x, and 3x in the corners).

The Corgi’s 1x image is never displayed, but the French Bulldog’s image behaves more predictably. Very cool, right? Give it a try by downloading the demo from GitHub.

Conclusion

Responsive images are an excellent feature of HTML, but they require a bit of thought upfront and an understanding of what you’re attempting to accomplish. The img tag has srcset and sizes and might be an easier path for ASP.NET Core devs as a starting point, but the picture tag offers more control and “art direction”. Regardless of your choice, you’ll deliver an optimized experience to your users.

Thanks for reading and sharing these posts with friends and colleagues. Cheers.