html-webpack-plugin 4 has been released!
Tags: webpack, html, javascript, webdev
Published: 2020-03-23 11:42:58

It took way too long but finally the new major version of the [`html-webpack-plugin`](https://www.npmjs.com/package/html-webpack-plugin) is making it's leap from beta to a stable release! 

## Performance!

![FlameGraph](https://user-images.githubusercontent.com/4573741/65042818-96609080-d951-11e9-84f4-046381549de8.png)

One big goal for this release was to improve the performance during development and production builds.

The performance boost was gained mainly by dropping the usage of `compilation.getStats().toJson()` thanks to a new API provided by the webpack core team around [**@sokra**](https://twitter.com/wSokra). This new API provides all information necessary to inject the scripts and styles into the html code.  
Unfortunately relying on that API means that **webpack 1-3 can not be supported anymore**.

To further increase the performance the entire caching approach was rebuilt from scratch to decrease the compilation efforts.

The alpha tester feedback was great!

[#953](https://github.com/jantimon/html-webpack-plugin/pull/953#issuecomment-417408140) "The best of 5 total build time goes down from 10.41s (with 4.0.0-alpha) to 10.29s - which is now **only 130ms slower than when not using the plugin at all** :-)"

[#962](https://github.com/jantimon/html-webpack-plugin/issues/962#issuecomment-409070361): "For reference, my project (which has grown substantially since #962) builds in **~8000ms without and ~1000ms with these changes**."

![CompileTime](https://dev-to-uploads.s3.amazonaws.com/i/msz8fmtz7d5zsmr2w32i.png)

While working on those performance improvements I wrote a [cpuprofile-webpack-plugin](https://github.com/jantimon/cpuprofile-webpack-plugin) - a small util to analyse your webpack build performance for production build but even more importantly for recompilations during development.

{% twitter 1149466691627130881 %}

Feel free to give it a try and let me know what you think :)

## Template language support

Since html-webpack-plugin 2.x has been able to use the loaders specified inside the webpack config file. Therefore it is not only capable of compiling `.ejs` templates but [any code which can be transpiled with a webpack loader](https://github.com/webpack/docs/wiki/list-of-loaders#templating) (`hbs`, `ejs`, `twig`, `dust`, `pug`, `htl`, `js,` `ts,` `jsx`, `tsx` ...).  

A javascript or jsx template allows even to generate a static server side rendered version of your application.  

html-webpack-plugin template:
```jsx
import ReactDOMServer from 'react-dom/server';
import React from 'react';
import { App } from './App';

export default () => `
  <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>Server Side Rendering Demo</title>
    </head>
    <body>
      <div id="root">${ReactDOMServer.renderToString(<App />)}</div>
    </body>
    </html>
`
```

For further details on how to connect loaders to the html-webpack-plugin checkout the [template option docs](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md) or take a look at the [html-webpack-plugin jsx codesandbox](https://codesandbox.io/s/html-webpack-plugin--jsx-sjbcq).

Another way to use the html-webpack-plugin to prerender a static page out of your app is the [prerender-loader](https://github.com/GoogleChromeLabs/prerender-loader) from [@developit](https://twitter.com/_developit).

## Meta Tags

The html-webpack-plugin is now able to inject meta-tags without writing custom templates:

```js
new HtmlWebpackPlugin({
  meta: {viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no'},
})
```

## Base Tags

Similar to the meta tags it is now also possible to add a base tag:

```js
new HtmlWebpackPlugin({
  base: '/',
})
```

## Custom Template with Zero Config

Customizing the template is now possible without configuration.  
From version 4 the html-webpack-plugin will look for a local `src/index.ejs` file. If such a file can be found it will be used as a template:

![Zero Config Template](https://dev-to-uploads.s3.amazonaws.com/i/08wr7cj5ty8nu8qot69y.png)


## Minification by default

Thanks to the work by [**@edmorley**](https://github.com/edmorley) we were able to enable html minification by default if webpack is running in `production` mode. This behaviour can be disabled by adding `minification: false` to the html-webpack-plugin configuration.

{% github https://github.com/jantimon/html-webpack-plugin/pull/1048 %}

![minification demo](https://dev-to-uploads.s3.amazonaws.com/i/rtvdfwc2whva4ay9vlr4.png)

## Allow template variable modification

[Evan You](https://twitter.com/youyuxi) asked for a better way to modify which values are sent down to the template:

{% github https://github.com/jantimon/html-webpack-plugin/pull/830#issue-160661993 %}

The outcome was a new `templateVariables` option which allows to add additional data e.g. `process.env` to the values which are sent down to the template:

![TemplateVariables](https://dev-to-uploads.s3.amazonaws.com/i/5xmkkukzdikgpn7npmyp.png)

## Nonblocking script loading

Until now all script tags were added at the end of the body tag.  
However now that all modern browsers allow to load javascript in parallel without pausing the html parsing the `scriptLoading` option can speed up your users page loads.

![defer](https://user-images.githubusercontent.com/4113649/76858525-e7e1cd80-6857-11ea-92e2-11f167e9462b.png)

Usage:

```js
new HtmlWebpackPlugin({
  scriptLoading: 'defer'
})
```


## New Hooks 

The webpack core team asked to upgrade to the new hook system to further increase the webpack build speed.

These hooks allow plugin developers to change the default behaviour of the html-webpack-plugin. The following chart shows the flow and the hooks (`beforeAssetTagGeneration`, `alterAssetTags`, `alterAssetTagGroups`, `afterTemplateExecution`, `beforeEmit`, `afterEmit`):

![Hook flow](https://raw.githubusercontent.com/jantimon/html-webpack-plugin/master/flow.png)

Here is an example for a plugin which manipulates the generated html file in the `beforeEmit` hook:

```js
const HtmlWebpackPlugin = require('html-webpack-plugin');

class MyPlugin {
  apply (compiler) {
    compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
      // Static Plugin interface |compilation |HOOK NAME | register listener 
      HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
        'MyPlugin', // <-- Set a meaningful name here for stacktraces
        (data, cb) => {
          // Manipulate the content
          data.html += 'The Magic Footer'
          // Tell webpack to move on
          cb(null, data)
        }
      )
    })
  }
}

module.exports = MyPlugin
```

For more information please take a look at the [readme events section](https://github.com/jantimon/html-webpack-plugin#events)

## Contributors

People around the world are approaching me to ask for help, suggest and work on new features, fix a typo or even fix entire problems. Thank you so much and please keep it up! :)

If you have any feedback for this release [create an issue](https://github.com/jantimon/html-webpack-plugin/) or contact me on twitter [@jantimon](https://twitter.com/jantimon) (direct messages are open).

## Sponsors

Big thanks to all the sponsors who supported the development over the last years.  
Especially [TipeIO](https://tipe.io/) and [Principal Financial Services, Inc](https://www.principal.com/)

[![Sponsors](https://dev-to-uploads.s3.amazonaws.com/i/xh6i2jrqqv96ht8te17s.png)](https://opencollective.com/html-webpack-plugin)

## Full changelog

The changelog with all changes can be found directly on [github]( https://github.com/jantimon/html-webpack-plugin/blob/master/CHANGELOG.md)

## What is coming up next?

The next goal is to be fully compatible with the Webpack 5. Especially with the new Webpack 5 `FileSystemInfo` API to solve {% github https://github.com/jantimon/html-webpack-plugin/issues/1269 %}.

Another goal would be to further improve browser load times. For browsers with support for preloading [**@sokra**](https://twitter.com/wSokra) proposed an even faster approach than `scriptLoading: 'defer'`.