Search code examples
javascripthtmlwebpackwebpack-dev-serverhtml-webpack-plugin

Webpack - How to include .html fragments into index.html with live reloading?


In PHP you can include file fragments for easy reusability. In the example below, I can include header.php and footer.php. This is very convenient because when I update header.php, the changes show up across all pages that use it:

<html>
<body>

    <?php include 'header.php';?>

    <p>Some text.</p>
    <p>Some more text.</p>

    <?php include 'footer.php';?>

</body>
</html>

I've successfully tried the approach in this answer by using html-webpack-plugin, but with one problem. See my config below:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const fs = require("fs");

module.exports = () => ({
    // ... lots of Webpack boilerplage

    module: {
        rules: [
            // ... js, sass, json, etc loaders
        ]
    },
    plugins: [

        //... 

        new HtmlWebpackPlugin({
            inject: false,
            hash: true,
            template: "./static/index.html",
            header: fs.readFileSync(htmlPath + "/header.html"),
            footer: fs.readFileSync(htmlPath + "/footer.html"),
            filename: "index.html",
        }),
    ]
});

This allows me to include my .html files like this:

<html>
<body>

    <%= htmlWebpackPlugin.options.header %>

    <p>Some text.</p>
    <p>Some more text.</p>

    <%= htmlWebpackPlugin.options.footer %>

</body>
</html>

It works as expected at first glance, but the included header.html and footer.html files get "locked" in their initial state, and if I modify them I still get the original files, not the updated version. I have to shut down the Webpack dev server, then re-run it for changes to come through. I'm guessing this is because fs.readFileSync() only gets executed when Webpack is initialized, but not after a file change has been detected. What can I do to get these files to update?


Solution

  • The solution was to move the fs.readFyleSync() calls from the webpack.config into my index.html file, since the config file is only executed once when the dev server is fired up.

    Here's my new webpack.config:

    // Get path to folder that contains HTML fragments
    const folderPath = path.resolve(__dirname, "./src/static/");
    
    module.exports = () => ({
        // ... lots of Webpack boilerplate
    
        plugins: [
            //... 
            new HtmlWebpackPlugin({
                inject: false,
                hash: true,
                template: "./static/index.html",
                filename: "index.html",
                HTML_PATH: folderPath // <- Now I only pass the folder path
            }),
        ]
    });
    

    ... and then I read the files with readFileSync() inside the HTML:

    <html>
    <body>
    
        <%= require("fs").readFileSync(htmlWebpackPlugin.options.HTML_PATH + "/header.html") %>
    
        <p>Some text.</p>
        <p>Some more text.</p>
    
        <%= require("fs").readFileSync(htmlWebpackPlugin.options.HTML_PATH + "/footer.html") %>
    
    </body>
    </html>
    

    Voilá! Hot-reloading HTML fragments!