How to optimize images in webpack

Images take more than a half of the size of an average page:

A pie chart. Title: “Average bytes per page per content type.” Text in the bottom: “Total 3422 kB”. The largest pie chart section: “Images – 1818 kB.”

That’s a lot of traffic! But with webpack, it’s easy to decrease it.

1. Inline small PNG, JPG and GIF images#

Use url-loader to embed small PNG, JPG and GIF images into the bundle.

url-loader converts a file (if it’s smaller than the specified size) into a Base64 URL and inserts this URL into the bundle. This helps to avoid extra image requests (which is useful even with HTTP/2).

The limit of 5-10 KB is OK:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        loader: 'url-loader',
        options: {
          // Images larger than 10 KB won’t be inlined
          limit: 10 * 1024
        }
      }
    ]
  }
};

2. Inline small SVG images#

Use svg-url-loader to embed small SVG images.

This loader works like url-loader, but it encodes files using the URL encoding instead of the Base64 one. Because SVG is text, the result of the URL encoding is smaller.

The limit of 5-10 KB is also OK:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: 'svg-url-loader',
        options: {
          // Images larger than 10 KB won’t be inlined
          limit: 10 * 1024,
          // Remove quotes around the encoded URL –
          // they’re rarely useful
          noquotes: true,
        }
      }
    ]
  }
};

3. Optimize image size#

Use image-webpack-loader to make images smaller.

This loader compresses PNG, JPG, GIF and SVG images by passing them through optimizers. Since it just pipes images through itself and doesn’t insert them into the bundle, it should be used with url-loader/svg-url-loader.

The default loader settings are OK:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpg|png|gif|svg)$/,
        loader: 'image-webpack-loader',
        // Specify enforce: 'pre' to apply the loader
        // before url-loader/svg-url-loader
        // and not duplicate it in rules with them
        enforce: 'pre'
      }
    ]
  }
};

Questions#

“Why is this important?”
As I’ve said, images take more than 50% of the average page size, and optimizing them is super-easy. Through, at the same time, I’ve rarely seen webpack configs that do it. We seriously should always optimize them.

“What should I do?”
Go and add these loaders to your config.

“Are the any side effects?”
A couple:

  • image-webpack-loader increases the build time, so it’s better to disable it during development (pass the bypassOnDebug: true option to do that)
  • url-loader and svg-url-loader remove extra image requests, but break caching of these images and increase the JS loading/parsing time and memory consumption. If you inline large images or lots of images, you might experience issues. (Thanks to Addy Osmani for noting this.)

Further reading#

Author: Ivan Akulov

I'm a software engineer specializing in web performance, JavaScript, and React. I’m also a Google Developer Expert. I work at Framer.