DEV Community

Marcin Wosinek
Marcin Wosinek

Posted on • Edited on

webpack 5 - tutorial for building multipage website

Have you ever wondered how to build multiple-page website with webpack? If that's your case - either due to maintaining a legacy page, or some current architectural decisions - the materials focused on single-page-app (SPA) can leave you with some doubts.

Define dynamically an entry for each page

In our case, we need to define one entry for each page. Assuming we have a list of pages in an array pages = ['a', 'b'], the entry: section of webpack config can look like:

  entry: pages.reduce((config, page) => {
    config[page] = `./src/${page}.js`;
    return config;
  }, {}),
Enter fullscreen mode Exit fullscreen mode

with just a bit of functional programming, we turned the pages list into:

{
  a: '.src/a.js',
  b: '.src/b.js'
}
Enter fullscreen mode Exit fullscreen mode

that we can set to entry. Because of doing it this way, the next time when we add a new page, it will be just adding one element to the list, without copy&pasting code.

Inject all the necessary code to html

Same as with SPAs, we want to inject the imports dynamically into your html. For that we useHtmlWebpackPlugin. Again, we want to use our pages array, so we avoid repeating code when we add new pages. So we will build our plugins: dynamically & we will leave a place to add some other, unrelated plugins there too.

  plugins: [].concat(
    pages.map(
      (page) =>
        new HtmlWebpackPlugin({
          inject: true,
          template: `./${page}.html`,
          filename: `${page}.html`,
          chunks: [page],
        })
    ),
    // <- here goes array(s) of other plugins
  ),
Enter fullscreen mode Exit fullscreen mode

Optimization

To get the most out of our architecture, we need to split built code into chunks. That will allow us to reuse portions of code if they are big enough and used across multiple pages. Luckily, we can achieve that by just adding:

  optimization: {
    splitChunks: {
      chunks: "all",
    },
  },
Enter fullscreen mode Exit fullscreen mode

Complete configuration & example app

The complete, working configuration:

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

const pages = ["a", "b"];

module.exports = {
  entry: pages.reduce((config, page) => {
    config[page] = `./src/${page}.js`;
    return config;
  }, {}),
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist"),
  },
  optimization: {
    splitChunks: {
      chunks: "all",
    },
  },
  plugins: [].concat(
    pages.map(
      (page) =>
        new HtmlWebpackPlugin({
          inject: true,
          template: `./${page}.html`,
          filename: `${page}.html`,
          chunks: [page],
        })
    )
  ),
};
Enter fullscreen mode Exit fullscreen mode

To play with it, the easiest way is to check out the repo of an example app:
https://github.com/marcin-wosinek/webpack-multipage-example

Links

Want's more?

Here you can find me going through the example with details:

Top comments (14)

Collapse
 
marta_johnsson profile image
martajohnsson

Thank You so much for sharing your knowlage. Your solution is almost perfect for my needs. I just wounder if You have any idea how to use it in dot.net MVC applications?
I have one layout template and my partial views have @Sripts sections where I would like to push chunks that webpack creates for me auto-magic :-) My views lives in Views map and I'm not really sure how to approach that problem.
Any ideas?

Collapse
 
marcinwosinek profile image
Marcin Wosinek

I have seen some dot.net applications using modern JS, but the integration was done on the dot.net side. No idea how webpack can be used in that scenario, I guess there should be one of those options possible:

  • using webpack output as static JS files
  • using some dot.net integration plugin to make it part of the build there
Collapse
 
marta_johnsson profile image
martajohnsson • Edited

I was able to solve it with HtmlWebpackPlugin and templates that building my script/style secion into partial views.
Had to add cutom html extension (found here: [stackoverflow.com/questions/543353...] ) to could render those sections in my partial, it is not allowed by default.
Working great. Thank You for the idea with HtmlWebpackPlugin, wouldn't come so far without that.

Collapse
 
marta_johnsson profile image
martajohnsson

Thank You.

Collapse
 
ourmaninindia profile image
Alfred Tuinman

So far so good but I am stuck as to how I can add database content at a pre-render stage so that ejs templating can be used? I don't want to do DOM manipulation.

Collapse
 
marcinwosinek profile image
Marcin Wosinek

sounds more like a job for a tool like NextJs. Doing it with webpack will be probably doable - the is plenty of loaders etc. but I'm afraid you will get to uncharted territory pretty fast.

Collapse
 
ourmaninindia profile image
Alfred Tuinman

I am a plain vanilla user so I don't think NextJs is my cup of tea.

Thread Thread
 
marcinwosinek profile image
Marcin Wosinek

What DB you want to connect to? Is it something that could be replaced with flat MD or text files in the repo?

Thread Thread
 
ourmaninindia profile image
Alfred Tuinman

actually I am using fetch to connect to the url which links to a MS-SQL database. It's a large db and the site I am converting from a Delphi based program to a microservice.

Thread Thread
 
marcinwosinek profile image
Marcin Wosinek

For me it sounds you are more in static page generation area, and less in the JS applications where webpack thrives.

You can consider:

  • writing a script to load the existing content, to include it in the new repository. Downside - you will replace DB with static files in the repo. This will change the workflow with content authoring. Once you have content locally, you will need to figure out importing it with wepback
  • use similar script, but on the build time. So data will live where you have it now, and each build will reload it's current state
  • write an application that loads data dynamically, from the browser
Collapse
 
norb48 profile image
Norb48

Hi Thanks for the nicely engineered code, not what i used in my project but I have a little problem while it works its a bit mess I guess it's my fault somewhere and I can't find the problem. I'm building small web for my photography only 2 html's.

This msg in console and functionality makes me headache , webpack-internal:///./src/imgslide.js:12 Uncaught TypeError: Cannot read properties of null (reading 'classList') at nextImg (imgslide.js:12:13) ,

obviously it can not read the 'classList' coz it doesn't exist in contact.html , it's different then index.html, can I ask you for some suggestions what might went wrong.
Gracias :)

Collapse
 
jackzhoumine profile image
jackzhoumine

how can use access the page in brower after config ? Dont need to change dev server config? but this so i can not see page?

Collapse
 
marcinwosinek profile image
Marcin Wosinek

I'm not sure if I understand the question. Do you want to start a dev server & access the website form the browser? Can you see any URLs the the output of the command that started the dev server?

Collapse
 
jackzhoumine profile image
jackzhoumine

yes it is my question. I fixed it. set dev open brower auto then accecss the page.