Support runtime data injection to a static markup produced by HtmlWebpackPlugin, using EJS
Tags: node, javascript, webpack, webdev
Published: 2021-04-23 14:57:26

#The requirements are simple
Let’s say you use Webpack [HtmlWebpackPlugin](https://github.com/jantimon/html-webpack-plugin) to produce a static HTML file from a template you have, but sometimes that’s not enough. This page that you’re producing is that main static page for your SPA (Single Page Application) and aside from the processed bundles, versioning etc.  you would like it to have other runtime data that concerns your entire site, for instance some sort of a configuration (not secret, god forbids) that needs to be available for scripts on the client.

#The challenge
In most cases such data, as the configuration mentioned above, is **runtime** data derived from environment configuration, while the template used by the HtmlWebpackPlugin allows you to inject **build-time** data to it. This means that it wouldn’t be wise (or possible in cases) to use the template injection of HtmlWebpackPlugin for these purposes, so how do we go about it?

#The common solution
Basically, when attempting to inject runtime data into a document that will be served to the client we turn to runtime templating engines. If your server runs on Node then a good option would be [EJS](https://ejs.co/). The server gets the request for the main document, loads the .ejs template file, renders it with the dynamic runtime data required and then returns it to the client.
So in theory we 3 phases here - 
1. Get HtmlWebpackPlugin to produce an .ejs file instead of .html file
2. Upon request, fetch that .ejs template and render it with runtime data
3. Return the result to the client

#So how do we go about it?
##Get HtmlWebpackPlugin to produce an .ejs file instead of .html file
This is quite simple, have your HtmlWebpackPlugin configuration define the result you want, like this:
```javascript
new HtmlWebpackPlugin({
    ...
    template: './index.template.html',
    filename: 'index.ejs',
}),
```
We declare where the template we’re using and the filename we wish it to have. Now when we run the build, the index.ejs file will be created according to the index.template.html file.

##Upon request, fetch that .ejs template and render it with runtime data
Now, when we get the call for the doc we do this - 
```javascript
const doc = await ejs.renderFile('./dist/index.ejs', {conf:'chuck'}, {delimiter: '?'});
reply.send(doc);
```

Here we take the .ejs template and render it with the dynamic runtime data which is defined on the current env (how to define it on your env and maintain prod and dev different configurations is a different story. If you wish me to dive into it, mention it in the comments).
This produces a doc which we can now send to the client.

Now, you might have noticed that “**delimiter**” property I passed to the ejs renderer. This is an important addition. You see, ejs is using the same default delimiter as the HtmlWebpackPlugin (“%”), and if you omit this property the HtmlWebpackPlugin will fail since it will try to interpolate keys in build time which are only available on runtime by a different templating engine. Therefore we use a different delimiter for ejs so there will not be any conflict.

That’s it. Be sure to leave you comments for any questions or feedback

Take care!