Migrating from HTML Webpack Plugin
For years, configuringhtml-webpack-plugin for multiple pages was a hassle.Each page required manually defining thechunks option to reference JavaScript files from the entry configuration,leading to a frustrating "chunks hell" configuration.
Also,html-webpack-plugin alone is not enough. Rendering an HTML template containing JS, CSS, SVG, images and other assets requires additional "crutches" as plugins forhtml-webpack-plugin, as well additional plugins and loaders.
Thus, this plugin requires a whole bunch of additional plugins and loaders, many of which have not been supported for a long time, which limits the use of the plugin itself.
To solve this and many other problems,was createdhtml-bundler-webpack-plugin — an "all-in-one" solution that simplifies HTML handlingand automatically manages asset dependencies with ease.
Multiple pages example
my-project/
├── dist/ (generated output)
├── src/
│ ├── scripts/
│ │ ├── main.js
│ ├── pages/
│ │ ├── home/
│ │ │ ├── index.html
│ │ │ ├── style.scss
│ │ │ ├── script.js
│ │ ├── about/
│ │ │ ├── index.html
│ │ │ ├── style.scss
│ │ │ ├── script.js
├── webpack.config.js
└── package.json
- src/pages/home/index.html
- src/pages/home/script.js
<!DOCTYPEhtml>
<htmllang="en">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>Home</h1>
</body>
</html>
// empty JS is only needed to import a page-specifically style
import'./style.scss'
Webpack config:
const path=require('path');
constMiniCssExtractPlugin=require('mini-css-extract-plugin');
constHtmlWebpackPlugin=require('html-webpack-plugin');
module.exports={
output:{
filename:'js/[name].[contenthash:8].js'
},
entry:{
main:'./src/scripts/main.js',
home:'./src/pages/home/script.js',
about:'./src/pages/about/script.js',
},
plugins:[
newMiniCssExtractPlugin({
filename:'css/[name].[contenthash:8].css',
}),
newHtmlWebpackPlugin({
template:'./src/pages/home/index.html',
filename:'index.html',// Output dist/index.html
title:'Home',
chunks:['main','home'],// Include scripts in this template
inject:'body',
}),
newHtmlWebpackPlugin({
template:'./src/pages/about/index.html',
filename:'about.html',// Output dist/about.html
title:'About',
chunks:['main','about'],// Include scripts in this template
inject:'body',
}),
],
module:{
rules:[
{
test:/\.s?css$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
};
Step by step
First,installhtml-bundler-webpack-plugin.
Step 1: Replace packages
Thehtml-bundler-webpack-plugin replaces the functionality ofhtml-webpack-plugin,mini-css-extract-plugin, and otherplugins and loaders:
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
- const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
Step 2: Remove loaders
Thehtml-bundler-webpack-plugin extracts CSS automatically.
RemoveMiniCssExtractPlugin.loader from the module rule:
{
test: /\.s?css$/,
use: [
- MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
Or if usedstyle-loader, remove it from the module rule:
{
test: /\.s?css$/,
use: [
- 'style-loader',
'css-loader',
'sass-loader',
],
},
warningEnsure that
css-loaderis first in theuse: []array and no other loaders come before it,because the plugin relies on the result ofcss-loaderto extract CSS.
Step 3: Replace plugin
Replace allHtmlWebpackPlugin with singleHtmlBundlerPlugin:
- Place the options object of each
HtmlWebpackPlugininstance intoentrycollection ofHtmlBundlerPlugin. - Rename the
templateproperty toimport. - Place template variables, e.g.
title, intodataoption. - Remove needless options, such as
chunks,chunksSortMode,excludeChunks,inject, etc.
plugins: [
- new HtmlWebpackPlugin({
- template: './src/pages/home/index.html',
- filename: 'index.html', // Output dist/index.html
- title: 'Home',
- chunks: ['main', 'home'], // Include scripts in this template
- inject: 'body',
- }),
- new HtmlWebpackPlugin({
- template: './src/pages/about/index.html',
- filename: 'about.html', // Output dist/about.html
- title: 'About',
- chunks: ['main', 'about'], // Include scripts in this template
- inject: 'body',
- }),
+ new HtmlBundlerPlugin({
+ entry: [
+ {
+ import: './src/pages/home/index.html',
+ filename: 'index.html', // Output dist/index.html
+ data: { title: 'Home', },
+ },
+ {
+ import: './src/pages/about/index.html',
+ filename: 'about.html', // Output dist/about.html
+ data: { title: 'About', },
+ },
+ ],
+ }),
],
Step 4: Move JS output filename
Move theoutput.filename to thejs.filename plugin option to keep related options at the same place:
module.exports = {
output: {
- filename: 'js/[name].[contenthash:8].js',
},
plugins: [
new HtmlBundlerPlugin({
entry: [ ... ],
+ js: {
+ filename: 'js/[name].[contenthash:8].js',
+ },
}),
],
};
Step 5: Move CSS output filename
Addcss.filename to the plugin options and removeMiniCssExtractPlugin:
module.exports = {
plugins: [
- new MiniCssExtractPlugin({
- filename: 'css/[name].[contenthash:8].css',
- }),
new HtmlBundlerPlugin({
entry: [ ... ],
js: { ... },
+ css: {
+ filename: 'css/[name].[contenthash:8].css',
+ },
}),
],
};
Step 6: Add<script> and<link> tags into HTML
Specify source script and style files in an HTML template using a relative path or a Webpack alias:
<!DOCTYPE html>
<html lang="en">
<head>
+ <link href="./style.scss" rel="stylesheet">
</head>
<body>
...
+ <script src="../../scripts/main.js"></script>
</body>
</html>
tipUseWebpack alias to avoid relative paths like
../../scripts/main.js:
- <script src="../../scripts/main.js"></script>
+ <script src="@scripts/main.js"></script>
Specify aliases in the Webpack config:
module.exports = {
+ resolve: {
+ alias: {
+ '@scripts': path.join(__dirname, 'src/scripts'),
+ },
+ },
};
Remove Webpack entry option, because your scripts and styles are specified directly in HTML:
module.exports = {
- entry: {
- main: './src/scripts/main.js',
- home: './src/pages/home/script.js',
- about: './src/pages/about/script.js',
- },
};
Optional: delete empty JS files containing imported styles only, because source styles specified directly in HTML.
infoThe plugin supports imported styles in JS, so you can leave it as is.
Step 7: Check template variables
The template variables defined in thedata option are available in a template w/o any prefix.Just removehtmlWebpackPlugin.options. in the template:
- <title><%= htmlWebpackPlugin.options.title %></title>
+ <title><%= title %></title>
tipTo pass a variable into all templates use the global
dataplugin option.
Final Webpack config
- NEW: using HtmlBundlerPlugin
- OLD: using HtmlWebpackPlugin
const path=require('path');
constHtmlBundlerPlugin=require('html-bundler-webpack-plugin');
module.exports={
resolve:{
alias:{
'@scripts': path.join(__dirname,'src/scripts'),
},
},
plugins:[
newHtmlBundlerPlugin({
entry:[
{
import:'./src/pages/home/index.html',
filename:'index.html',// Output dist/index.html
data:{title:'Home',},
},
{
import:'./src/pages/about/index.html',
filename:'about.html',// Output dist/about.html
data:{title:'About',},
},
],
js:{
filename:'js/[name].[contenthash:8].js',
},
css:{
filename:'css/[name].[contenthash:8].css',
},
}),
],
module:{
rules:[
{
test:/\.s?css$/,
use:[
'css-loader',
'sass-loader',
],
},
],
},
};
const path=require('path');
constMiniCssExtractPlugin=require('mini-css-extract-plugin');
constHtmlWebpackPlugin=require('html-webpack-plugin');
module.exports={
output:{
filename:'js/[name].[contenthash:8].js'
},
entry:{
main:'./src/scripts/main.js',
home:'./src/pages/home/script.js',
about:'./src/pages/about/script.js',
},
plugins:[
newMiniCssExtractPlugin({
filename:'css/[name].[contenthash:8].css',
}),
newHtmlWebpackPlugin({
template:'./src/pages/home/index.html',
filename:'index.html',// Output dist/index.html
title:'Home',
chunks:['main','home'],// Include scripts in this template
inject:'body',
}),
newHtmlWebpackPlugin({
template:'./src/pages/about/index.html',
filename:'about.html',// Output dist/about.html
title:'About',
chunks:['main','about'],// Include scripts in this template
inject:'body',
}),
],
module:{
rules:[
{
test:/\.s?css$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
};