Updated 2023-10-02
Usually, a junior web developer is faced with the problem of using a complex Webpack setup to create a simple HTML page with JS and CSS. First, you need to know where and how to connect source styles and scripts with HTML, and where to define the HTML template itself. You need to know what plugins and loaders are needed for this and how to configure them. The whole process isn’t simple or intuitive. Moreso, configurations happen in different places.
Until now, it was necessary to use such plugins and loaders as:
Package |
Description |
---|---|
html-webpack-plugin |
creates HTML and inject |
mini-css-extract-plugin |
injects |
webpack-remove-empty-scripts |
removes generated empty JS files |
html-loader |
exports HTML |
style-loader |
injects an inline CSS into HTML |
posthtml-inline-svg |
injects an inline SVG icon into HTML |
resolve-url-loader |
resolves a relative URL in CSS |
svg-url-loader |
encodes a SVG data-URL as utf8 |
Finally, the new HTML Bundler Plugin for Webpack has emerged, replacing this zoo of plugins and making Webpack setup incredibly simple, logical, and intuitive. What’s more? all the config happens in one place.
This plugin allows you to use an HTML template as a starting point for all the dependencies used in your web application. All source styles and scripts specified in HTML are processed, and the extracted JS and CSS are saved to the output directory.
For example, you have an HTML template containing a script, a style, and an image.
You can add script and style source files directly to HTML using a relative path or a Webpack alias:
<html>
<head>
<!-- load source style here -->
<link href="./style.scss" rel="stylesheet">
<!-- load source script here -->
<script src="./main.js" defer="defer"></script>
</head>
<body>
<h1>Hello World!</h1>
<!-- @images is Webpack alias for the source images directory -->
<img src="@images/logo.png">
</body>
</html>
The source files are processed using Webpack loaders and the plugin automatically substitutes output filenames into the generated HTML file:
<html>
<head>
<link href="assets/css/style.05e4dd86.css" rel="stylesheet">
<script src="assets/js/main.f4b855d8.js" defer="defer"></script>
</head>
<body>
<h1>Hello World!</h1>
<img src="assets/img/logo.58b43bd8.png">
</body>
</html>
In the Webpack config define an HTML template as the entry point in the entry
option:
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
},
resolve: {
alias: {
// aliases used in the template
'@images': path.join(__dirname, 'src/images'),
},
},
plugins: [
new HtmlBundlerPlugin({
// define a relative or absolute path to entry templates for
// automatically processing templates in all subdirs
entry: 'src/views/',
// - OR - define many templates manually
entry: {
// output => dist/index.html
index: 'src/views/home/index.html',
// output => dist/pages/about.html
'pages/about': 'src/views/about/index.html',
// ...
},
js: {
// output filename of compiled JavaScript,
// used if `inline` option is false (defaults)
filename: 'assets/js/[name].[contenthash:8].js',
//inline: true, // inlines JS into HTML
},
css: {
// output filename of extracted CSS,
// used if `inline` option is false (defaults)
filename: 'assets/css/[name].[contenthash:8].css',
//inline: true, // inlines CSS into HTML
},
}),
],
module: {
rules: [
// styles
{
test: /\.(css|sass|scss)$/,
use: ['css-loader', 'sass-loader'],
},
// images
{
test: /\.(png|jpe?g|ico|svg)$/,
type: 'asset/resource',
generator: {
filename: 'assets/img/[name].[hash:8][ext]',
},
},
],
},
};
Note: No additional loaders for html templates are required, the plugin can self load templates.
The HTML Bundler Plugin supports most popular template engines such as EJS, Eta, Handlebars, Nunjucks “out of the box“.
For example, there is a Handlebars template file index.html:
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ headline }}</h1>
<div>
<p>{{ firstname }} {{ lastname }}</p>
</div>
</body>
</html>
To compile a Handlebars template into HTML use the preprocessor option to specify the templating engine:
// ...
plugins: {
new HtmlBundlerPlugin({
// ...
preprocessor: 'handlebars',
}),
},
// ...
For details of the preprocessor
option see here.
You can pass variables into the template using the advanced syntax of the entry
option and the data
property:
new HtmlBundlerPlugin({
entry: {
index: { // => the key is the HTML output filename w/o extension
import: './src/views/home/index.html',
// pass data into the template (via preprocessor)
data: {
// ... variables as key:value
},
},
},
}),
The complete Webpack configuration:
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
output: {
path: path.join(__dirname, 'dist/'),
},
plugins: [
new HtmlBundlerPlugin({
entry: {
index: {
// import template file
import: './src/views/home/index.html',
data: {
// pass variables into template
title: 'Heisenberg',
headline: 'Breaking Bad',
firstname: 'Walter',
lastname: 'White',
},
},
},
// use the supported templating engine
preprocessor: 'handlebars',
}),
],
// ... module rules for styles, images, fonts, etc.
};
The generated HTML:
<html>
<head>
<title>Heisenberg</title>
</head>
<body>
<h1>Breaking Bad</h1>
<div>
<p>Walter White</p>
</div>
</body>
</html>
View an example used the Handlebars template in browser: Open in StackBlitz.
You can use any templating engine via the preprocessor option as a callback function.
For example, using a Mustache termplate**:**
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
const Mustache = require('mustache');
module.exports = {
// ...
plugins: [
new HtmlBundlerPlugin({
entry: {
index: {
import: './src/views/index.html', // Mustache template
data: {...}, // passed data
},
},
preprocessor: (template, { data }) => Mustache.render(template, data),
}),
],
};
Thus, you can use the latest version of any template engine, independent of whether a Webpack loader exists for it and how up-to-date it is.
Often, many original loaders have not been updated for more than 2 years and may contain outdated versions of these templating engines. This means, that you can't use features (or bugfixes) of the last version of a template engine.