1. 1. The simplest way…
  2. 2. Project setup
  3. 3. Include templates function
  4. 4. Webpack config
    1. 4.1. Multiple HTML files - done!

Generating multiple HTML pages with HTMLWebpackPlugin

HTMLWebpackPlugin is a simple way to generate the html files for your web app. Using HTMLWebpackPlugin is covered in another post.

It is possible to generate multiple HTML files with HTMLWebpackPlugin and it is relatively simple. This will be based off the same project configuration as Using HTMLWebpackPlugin and Pug, and will be using pug.js - but it should work the same for however you are generating your HTML files.

The simplest way…

To generate multiple HTML files, you have to add multiple HTMLWepbackPlugins to your webpack plugins. Like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* webpack.config.js */
...
plugins: [
// Extract our css to a separate css file
new ExtractTextPlugin('css/styles.css'),
// Use HTMLWebpackPLugin with template set to our pug index template.
// Index is the default name so we don't need to set it for our index template
new HTMLWebpackPLugin({
template: './src/template/index.pug'
}),
// We need to set our desired filename for other html files though.
new HTMLWebpackPLugin({
filename: 'services.html',
template: './src/template/services.pug'
}),
new HTMLWebpackPLugin({
filename: 'contact.html',
template: './src/template/contact.pug'
})
]
...

This works well when you only have a couple of files to generate, but is a bit awkward to maintain as you add more files. So is there a more elegant way to achieve this?

Yes, there is!!

We can achieve this in an easier to maintain way, and without adding much complexity to our build setup.

Project setup

The project setup will be almost identical to the Using HTMLWebpackPlugin and Pug post. The only difference will be the templates directory.

| Source files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
webpack.config.js
src/
├── main.js
├── styles.css
└── templates/
├── includes/
│ ├── foot.pug
│ ├── head.pug
│ └── layout.pug
└── views/
├── index.pug
├── products.pug
├── services.pug
└── contact.pug

As you can see the main difference is that our templates are all in a views subfolder and everything else is in includes. The important takeaway here, is that we want all the template files that correspond to our desired html files in a folder on their own.

Include templates function

To accomplish our task we will write a simple function to read the files from our views directory and generate an array of HTMLWebpackPlugins.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function generateHtmlPlugins (templateDir) {
const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir))
return templateFiles.map(item => {
// Split names and extension
const parts = item.split('.')
const name = parts[0]
const extension = parts[1]
return new HTMLWebpackPlugin({
filename: `${name}.html`,
template: path.resolve(__dirname, `${templateDir}/${name}.${extension}`)
})
})
}

// We will call the function like this:
const htmlPlugins = generateHtmlPlugins('./src/template/views')

Webpack config

With the generateHtmlPlugins function added our webpack configuration file will look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* webpack.config.js */
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// We need Nodes fs module to read directory contents
const fs = require('fs')

// Our function that generates our html plugins
function generateHtmlPlugins (templateDir) {
// Read files in template directory
const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir))
return templateFiles.map(item => {
// Split names and extension
const parts = item.split('.')
const name = parts[0]
const extension = parts[1]
// Create new HTMLWebpackPlugin with options
return new HTMLWebpackPlugin({
filename: `${name}.html`,
template: path.resolve(__dirname, `${templateDir}/${name}.${extension}`)
})
})
}

// Call our function on our views directory.
const htmlPlugins = generateHtmlPlugins('./src/template/views')

module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'), // Output folder
filename: 'js/app.js' // JS output path
},
module: {
rules: [
// Include pug-loader to process the pug files
{
test: /\.pug$/,
use: 'pug-loader'
},
// Include css/style loaders to process our css files
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}
]
},
plugins: [
// Extract our css to a separate css file
new ExtractTextPlugin('css/styles.css')
]
// We join our htmlPlugin array to the end
// of our webpack plugins array.
.concat(htmlPlugins)
}

And at the end there you should notice that we simply concatenate our htmlPlugins array to our webpackConfig.plugins array.

Multiple HTML files - done!

If we run webpack now, we will see that all our html files are generated (index, services, products and contact). Success!

And all that is required to add/generate more html files, is to create more template files in the views directory and name them correctly (‘products.pug’ becomes ‘products.html’).

A useful note: while in watch mode files added to the directory will not be picked up, but changes to template files included this way will be.