Optimized image loading in HTML and Markdown
Tags: webdev, html, javascript, tutorial
Published: 2020-09-12 18:29:03

This is the third article about image optimization I implemented in my blog on Nuxt with Netlify CMS. 

In this article, I’m going to talk about how I implemented all this on my website. What worked and what didn’t.

## Images in HTML

Let’s start with the images set using HTML. 

This is how it’s done with me:

```html
<img
  src="/img/sample_image.png?nf_resize=fit&w=250"
  loading="lazy"
  alt=". . ."
/>
```

### Loading optimized images from Netlify Large Media

I add `?nf_resize&w=250` to load an image with a width reduced to 250 pixels and proportional heigh. 

Why I chose `250px`? I checked what is the maximum width of the particular image when it’s rendered and set the width accordingly. 

On my site, images usually are shown in different sizes. For desktop browsers, it’s fixed width. On mobile, it’s wired to screen width, so it could range from `30px` to `236px` (something like this), so I simply chose `250px` since it never gets bigger than this.

Additional parameters that can be specified on image request from Netlify Large Media you may find in [docs](https://docs.netlify.com/large-media/transform-images/#request-transformations).

### Lazy loading

Adding `loading="lazy"` say that browser should load an image only when it’s close to the viewable viewport. 

When I didn’t set that at the beginning, the page loaded in the following way: on page open, browser paints the first HTML and starts loading all images used on the current page and until it finishes page are shown as loading. If the page contains many pictures, that take time. Sometimes a lot. 

When you set `loading="lazy"` browser loads only images that are in the viewable part of the page and in the area of `1250px`-`2500px` down it ([doc](https://web.dev/native-lazy-loading/#improved-data-savings-and-distance-from-viewport-thresholds)). At least on Chromium browsers it should work this way. 

So, yeah, it could speed up page load a lot, since loading first 3 images and then others, as needed, is faster than loading more at once😉.

However, I didn’t set this for the first images on the pages, as advised officially [here](https://web.dev/native-lazy-loading/#avoid-lazy-loading-images-that-are-in-the-first-visible-viewport). These images are anyway going to be loaded on the page open🙂.

### Trying `srcset` and `sizes`

I also tried to do responsive image loading with the following code from [Mozilla](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images):

```html
<img srcset="path_to_image.png?nf_resize=fit&w=180 180w,
             path_to_image.png?nf_resize=fit&w=250 250w"
     sizes="(max-width: 400px) 180px,
            250px"
     src="path_to_image.png?nf_resize=fit&w=250"
     alt=". . ."
/>
```

As from code, it means that on screens with a width up to 400 pixels browser should request an image with this parameters: `?nf_resize=fit&w=180 180w`. Hence with a width of 180 pixels. While on screens with a width of more than 400 pixels, it should load an image with this parameters: `?nf_resize=fit&w=250 250w`. So the width should be 250 pixels. 

But these didn’t work. 

When specifying `sizes` in the percentage of `vw` (viewport), everything works, as you will see later on with Markdown images. But with `px` specifications nothing worked. Images were always loading with this parameters: `?nf_resize=fit&w=250 250w`🤷‍♂️.

After playing with it for some time, I understood that I could simply leave it with one `?nf_resize=fit&w=250 250w` parameter. As I had before, like this:

```html
<img
  src="/img/sample_image.png?nf_resize=fit&w=250"
  loading="lazy"
  alt=". . ."
/>
```

My images become really responsive on mobile, and figuring out correct `vw` for different layouts is a considerable pain (at least for me).

I have also downloaded images to compare their sizes. `250px` one was 114 kb, where `180px` one was 63,3 kb. Quite twice a difference, but after running Page Speed tests with `180px` image, I didn’t see any improvements🤷‍♂️.

## Loading optimized images from Markdown

Doing all this optimization for blog posts is a bit more complicated. 

All my posts are written in Markdown and gets converted into HTML by [markdown-it](https://www.npmjs.com/package/markdown-it) plugin. 

Markdown language has some specifications and limitations on how images are described. Here is the structure of the image specification: `![Alt text people will see if picture can’t be seen](https://link.of/image “Title to show under the image”)`. Not so many things we could specify. Luckily we can do a lot of modification on how Markdown is translated to HTML with the additional `markdown-it-` plugins. 

### Lazy images

First of all, I found and added a plugin that adds `loading=”lazy”` to every image that is rendered by markdown-it. 

Here is it: [markdown-it-image-lazy-loading](https://www.npmjs.com/package/markdown-it-image-lazy-loading). **!** If you are also planning to load optimized images by adding URL parameters to images, then wait a bit before adding it. There is a way to use only the plugin I will show next without the need to install this one. Just proceed to the next section.

After setting `loading="lazy"` loading speed of blog pages with many images rocketed. This attribute is really must-have. Check the results in the [next article](https://dev.to/nikitagoncharuk/do-optimised-images-always-bring-load-speed-improvements-2pbb).

### Images with `srcset` and `sizes`

Then I tried adding [markdown-it-responsive](https://www.npmjs.com/package/@davegardner/markdown-it-responsive) package that should add `srcset` and `sizes` attributes to every image, but this didn’t work at all. I have been getting HTML rendering error and broken pages☹️.

After some additional search, I have found this plugin: [markdown-it-modify-token](https://www.npmjs.com/package/markdown-it-modify-token). After checking it, I understood that it would work great, and I can do everything I need with it.

Some time and this code was born:

```js
modifyToken: function(token, env) {
  switch (token.type) {
    case "image":
      token.attrObj.srcset =
        `${token.attrObj.src}?nf_resize=fit&w=300 300w, ` +
        `${token.attrObj.src}?nf_resize=fit&w=600 600w`;
      token.attrObj.src = token.attrObj.src + "?nf_resize=fit&w=600";
      break;
  }
},
```

By specifying `srcset` this way I’m saying to the browser: here is two images, with the width of `300px` and `600px`, decide by yourself what image to load according to 100% viewport width. 

First I also added `size` attribute, this way:

```js
token.attrObj.sizes = “100vw”;
```

But removed it after reading Chris Coyier [article](https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/). It’s the default value for the browser, so no need to specify that additionally.

It works, but the behaviour is a bit strange (at least for me). When screen width is `200px` browser load image with `300px` width, but when screen width is set to `250px` image with `600px` width is loaded… I don’t understand that😐. 

And again specifying `sizes` in `px` only led to `600px` image to be loaded… 

Here is code I have tried:

```js
token.attrObj.sizes = "(max-width: 400px) 300px, 600px"
```

Ok, I will just leave sizes as `100vw` and let the browser decide when to load what. Hope browser is smart.

As I wrote before, the usage of [markdown-it-image-lazy-loading](https://www.npmjs.com/package/markdown-it-image-lazy-loading) plugin could be dropped here for additional code in 

```js
modifyToken: function(token, env) {

}
```

Just add this:

```js
token.attrObj.loading = "lazy";
```

Here, in `case "image":`:

```js
switch (token.type) {
  case "image":
    token.attrObj.srcset =
      `${token.attrObj.src}?nf_resize=fit&w=300 300w, ` +
      `${token.attrObj.src}?nf_resize=fit&w=600 600w`;
    token.attrObj.src = token.attrObj.src + "?nf_resize=fit&w=600";
          
    // This will make all images loading lazy
    token.attrObj.loading = "lazy";
    break;
}
```

By the way, if you want to always load images with one size, just remove `srcset` setter. It would look this way:

```js
switch (token.type) {
  case "image":
    token.attrObj.src = token.attrObj.src + "?nf_resize=fit&w=600";
    // token.attrObj.loading = "lazy";
    break;
}
```

As you remember, it’s better to have the first images without lazy load, but it’s a bit hard to do with images from Markdown. Additional logic should be written for markdown-it, and the time investment is not really worth it for me. Yes, there is a small drop a performance, as guys from Google say, but it won’t break the bank, I think.  

That’s all I have done for better image loading on my site. In the [next article](https://dev.to/nikitagoncharuk/do-optimised-images-always-bring-load-speed-improvements-2pbb), you could check page speed improvements I got. They are quite solid😉.

To see more behind-the-curtain of my blog creation **[join me on Twitter](https://twitter.com/nikitago_)** as I continue sharing how I’m making it.

*By the way, if you know why `sizes` can only work well with `vw`, share it in comments. I'm really interested!*