Optimizing Static HTML And Images With Webpack (2024)

Webpack is great for building JavaScript applications, but did you know it can optimize static HTML assets too? In this article, we use Webpack to process HTML files while automatically optimizing their images too.

Hey! Don’t want to read all the in’s and outs of bending Webpack into shape? Jump to the final webpack configuration.

Sometimes Static Html Is Best

Webpack works great for building JavaScript heavy sites. But even in a complex JavaScript application, some pages are simpler to create and serve as static HTML. However, Webpack entirely ignores HTML files out of the box. With a little work, Webpack can process HTML files just like it does JavaScript files. We can even optimize images referenced in the HTML!

A Simple Static Site

We’ve built a small set of static HTML files and images to demonstrate how Webpack handles HTML and the images it references:

Optimizing Static HTML And Images With Webpack (1)

The index.js JavaScript file is empty and simply there to make Webpack happy. Our HTML files look like this:

<html> <head> <title>Static HTML With Inline Images</title> </head> <h2>Static HTML With Inline Images</h2> <p> <h3>Links:</h3> <a href="page1.html"> <img src="images/small-puppy.png"> Page 1 </a><br> <a href="sub_directory/page2.html"> <img src="images/small-chick.png"> Page 2 </a><br> <a href="sub_directory/sub_directory2/page3.html"> <img src="images/small-tree.png"> Page 3 </a> </p></html>
<html> <head> <title>Page 2: Html In A Sub-Directory</title> </head> <h2><img src="../images/small-chick.png">Page 2: Html In A Sub-Directory</h2> <p> <h3><a href="../index.html">Back</a></h3> </p> <p> A larger image:<br> <img src="../images/medium-duckling.jpg"> </p></html>

Processing Static HTML With Webpack

Webpack doesn’t know how to handle HTML files, but adding the html-webpack-plugin lets Webpack parse HTML files and place them into the output directory. First we need to install dependencies with NPM:

npm install --save-dev webpack webpack-cli html-webpack-plugin# We'll use these two loaders later to load and optimize images:npm install --save-dev html-loader image-webpack-loader

With dependencies installed, we can configure the plugin in webpack.config.js to include index.html in Webpack’s output.

var path = require('path');var HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = { mode: 'development', entry: './src/index.js', output: { path: path.resolve('dist/'), clean: true }, module: { }, plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html', filename: 'index.html', inject: false }) ],};

After this change, Webpack has dutifully included a single html file in the output directory along with an empty JS file:

Optimizing Static HTML And Images With Webpack (2)

Automatically Processing Many HTML Files

There is one HTML file in the output directory, but there are three more files missing. It would be tedious and error prone to update Webpack’s config every time files are added or removed. Instead of manually adding the other files, let’s modify the configuration to automatically include all HTML files it finds. It’s not immediately obvious how to accomplish this because a HtmlWebpackPlugin instance only handles a single HTML file (It doesn’t support globbing).

Luckily, Webpack’s config is not just a bag of JSON, but a full-fledged JavaScript file run by Node.js. Using this fact, we can add logic that crawls our source directory looking for HTML files. Some of the files are in subdirectories so we look through those as well:

var path = require('path');var fs = require('fs');var HtmlWebpackPlugin = require('html-webpack-plugin');// Look for .html filesvar htmlFiles = [];var directories = ['src'];while (directories.length > 0) { var directory = directories.pop(); var dirContents = fs.readdirSync(directory) .map(file => path.join(directory, file)); htmlFiles.push(...dirContents.filter(file => file.endsWith('.html'))); directories.push(...dirContents.filter(file => fs.statSync(file).isDirectory()));}module.exports = { mode: 'development', entry: './src/index.js', output: { path: path.resolve('dist/'), clean: true }, module: { }, plugins: [ // Build a new plugin instance for each .html file found ...htmlFiles.map(htmlFile => new HtmlWebpackPlugin({ template: htmlFile, filename: htmlFile.replace(path.normalize("src/"), ""), inject: false }) ) ],};

Webpack now automatically finds all static html files and outputs them to the dist/ directory:

Optimizing Static HTML And Images With Webpack (3)

Processing Images With Webpack

Webpack is automatically processing our HTML, but doesn’t do anything with the images referenced in img tags:

Optimizing Static HTML And Images With Webpack (4)

The Webpack config needs two additional module rules to get images working. The first rule parses HTML files using html-loader which allows Webpack to look for image references. The second uses a Webpack 5 Asset Module to copy those images to the output directory:

module.exports = { // ...snip... module: { rules: [ { test: /\.html$/i, use: 'html-loader' }, { test: /\.(png|jpg)$/i, type: 'asset/resource', generator: { filename: 'images/[name]-[hash][ext]' } } ] }, // ...snip...};

Now all images referenced in the HTML are output to their own directory:

Optimizing Static HTML And Images With Webpack (5)

Images now display correctly and we can navigate between pages:

Optimizing Static HTML And Images With Webpack (6)

Optimizing Image File Size

Because Webpack is running images through its pipeline, we can do more than just copy them. Adding image-webpack-loader to the existing image rule will reduce image file size on the fly. By default, image-webpack-loader optimizes JPEG, PNG, SVG and GIF images. We’ve overridden the PNG options to get smaller files at the cost of a small reduction in quality:

module.exports = { // ...snip... module: { rules: [ // ...snip... { test: /\.(png|jpg)$/i, type: 'asset/resource', use: [{ loader: 'image-webpack-loader', options: { pngquant: { quality: [.90, .95], }, } }], generator: { filename: 'images/[name]-[hash][ext]' } } ] }, // ...snip...};

After optimization, the output images are almost half the size of the originals:

Optimizing Static HTML And Images With Webpack (7)

Automatically Inlining Images

We can further optimize image loading by embedding small images directly in the HTML. Image inlining improves page load time by reducing the number of requests the browser makes. Webpack 5’s Asset Module comes with built-in support for automatic inlining of small assets. We can enable it by updating our image rule:

{ test: /\.(png|jpg)$/i, type: 'asset', // ...snip... parser: { dataUrlCondition: { maxSize: 10 * 1024 // Inline anything under 10kb } }, // ...snip...}

The smaller images are missing from the image output directory after this change. The only images left are too big to embed:

Optimizing Static HTML And Images With Webpack (8)

The missing images have been embedded directly into the HTML files:

Optimizing Static HTML And Images With Webpack (9)

TLDR: Final Webpack Configuration

Webpack is automatically parsing all our static HTML, optimizing referenced images, and inlining those images when it makes sense. Put together, the final Webpack configuration looks like this:

var path = require('path');var fs = require('fs');var HtmlWebpackPlugin = require('html-webpack-plugin');// Look for .html filesvar htmlFiles = [];var directories = ['src'];while (directories.length > 0) { var directory = directories.pop(); var dirContents = fs.readdirSync(directory) .map(file => path.join(directory, file)); htmlFiles.push(...dirContents.filter(file => file.endsWith('.html'))); directories.push(...dirContents.filter(file => fs.statSync(file).isDirectory()));}module.exports = { mode: 'development', entry: './src/index.js', output: { path: __dirname + '/dist', clean: true }, module: { rules: [ { test: /\.html$/i, use: 'html-loader' }, { test: /\.(png|jpg)$/i, type: 'asset', use: [{ loader: 'image-webpack-loader', options: { pngquant: { quality: [.90, .95], }, } }], parser: { dataUrlCondition: { maxSize: 10 * 1024 // 10kb } }, generator: { filename: 'images/[name]-[hash][ext]' } } ] }, plugins: [ // Build a new plugin instance for each .html file found ...htmlFiles.map(htmlFile => new HtmlWebpackPlugin({ template: htmlFile, filename: htmlFile.replace(path.normalize("src/"), ""), inject: false }) ) ],};

Conclusion

Our static HTML and image assets are now parsed and optimized by Webpack. Did the changes improve the performance of these pages? That’s what Request Metrics is for! Try it out for free to learn the real performance of your production website.

Optimizing Static HTML And Images With Webpack (2024)

FAQs

How do I optimize my webpack bundle? ›

Easy: Run webpack in production mode
  1. Enable minification using TerserPlugin. It removes unnecessary white spaces, new lines, unreachable code, etc.
  2. Set the NODE_ENV to production . This tells some packages, like React, to not include debug code.

How to include webpack in html? ›

Basic Usage. If you have multiple webpack entry points, they will all be included with <script> tags in the generated HTML. If you have any CSS assets in webpack's output (for example, CSS extracted with the MiniCssExtractPlugin) then these will be included with <link> tags in the <head> element of generated HTML.

Why do we need html webpack plugin? ›

Webpack 4. This is a webpack plugin that simplifies creation of HTML files to serve your webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation.

How to install html-loader? ›

To begin, you'll need to install html-loader :
  1. npm install --save-dev html-loader. or.
  2. yarn add -D html-loader. or.
  3. pnpm add -D html-loader. Then add the plugin to your webpack config. For example: file.js. import html from "./file.html"; ...
  4. // => image.jpg in __dirname/fixtures will be resolved. CDN. webpack.config.js. module.
Jan 16, 2024

How to improve performance using webpack? ›

Try to keep chunks small.
  1. Use fewer/smaller libraries.
  2. Use the SplitChunksPlugin in Multi-Page Applications.
  3. Use the SplitChunksPlugin in async mode in Multi-Page Applications.
  4. Remove unused code.
  5. Only compile the part of the code you are currently developing on.

Does webpack optimize? ›

Webpack runs optimizations for you depending on the chosen mode , still all optimizations are available for manual configuration and overrides.

What does HTML loader do in webpack? ›

By default, html-loader generates JS modules that use the ES modules syntax. There are some cases in which using ES modules is beneficial, such as module concatenation and tree shaking.

How to link CSS in webpack? ›

To be able to use CSS in your webpack app, you need to set up a new loader. Out-of-the-box, webpack only understands Javascript and JSON. With a loader, you can translate another type of file to a format that webpack understands and can work with. There are many webpack loaders and each loader has a specific purpose.

Where does webpack put files? ›

Webpack will generate the files and put them in the /dist folder for you, but it doesn't keep track of which files are actually in use by your project. In general it's good practice to clean the /dist folder before each build, so that only used files will be generated.

Is webpack good or bad? ›

Conclusion. Webpack as a bundler is excellent for use in a multitude of applications. Especially if someone else handles the config for you (Angular, React, Vue clis). It will hopefully become better, but like anything else in JS its roots and backwards compatibility will always bring it down.

When should I use webpack? ›

So, if you would like to write more modular code— code that is broken across multiple files for resuability, readability, and/or maintain ability (i.e. you don't want to dig through one file and 1000 lines of code, when you could divide your code into multiple files)—then webpack is perfect.

Is webpack really necessary? ›

Luckily, Webpack can manage all those dependencies for us and conveniently bundle them into a single JavaScript bundle that includes all the code needed for the application. Then you need to include this JavaScript bundle inside your HTML and this bundle will contain all your dependencies.

What is copy webpack plugin? ›

copy-webpack-plugin is not designed to copy files generated from the build process; rather, it is to copy files that already exist in the source tree, as part of the build process.

Which feature in webpack allows for automatic injection of bundles into an HTML file? ›

The html-webpack-plugin will automatically inject all necessary CSS, JS, manifest and favicon files into the markup. Details of other template loaders are documented here. If you already have a template loader, you can use it to parse the template.

Why use HTML loader? ›

The html-loader defination says that it exports html as String (What does it mean). it also says that every loadable attributes (for example <img src="image. png" is imported as require('./image. png') ,and you may need to specify loader for images in your configuration ( file-loader or url-loader ), What does it mean.

Why is my webpack bundle so big? ›

Webpack works by building a dependency graph of every module imported into our web app, traversing through files containing the code we need, and bundling them together into a single file. As our app grows in complexity with more routes, components, and dependencies, so does our bundle.

How do I reduce the size of my bundle? ›

Minification is a key step to reduce the bundle size in JavaScript. It's about removing unnecessary things like spaces and comments and renaming variables to be shorter, but without changing how the code works. Tools like UglifyJS and Terser help with this, making JavaScript smaller and more efficient.

What is bundle optimization? ›

Bundle Optimization refers to the process of deciding which symbol goes to which bundle so that the application can co-locate the code that is used together. Having the symbols co-located can make the application load faster.

What is a good JS bundle size? ›

The larger your js bundle size, the longer it takes for your website to load, especially on slower or mobile devices. This can affect your website's performance, usability, and SEO ranking. According to Google, a js bundle size of more than 170 KB can have a significant impact on your website's speed and bounce rate.

Top Articles
Latest Posts
Article information

Author: Foster Heidenreich CPA

Last Updated:

Views: 5782

Rating: 4.6 / 5 (76 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Foster Heidenreich CPA

Birthday: 1995-01-14

Address: 55021 Usha Garden, North Larisa, DE 19209

Phone: +6812240846623

Job: Corporate Healthcare Strategist

Hobby: Singing, Listening to music, Rafting, LARPing, Gardening, Quilting, Rappelling

Introduction: My name is Foster Heidenreich CPA, I am a delightful, quaint, glorious, quaint, faithful, enchanting, fine person who loves writing and wants to share my knowledge and understanding with you.