How to build a multipage website with esbuild

In this article, I will show how to set a simple multipage website with esbuild. The code used here is similar to the one I used in this webpack 5 example, so you can use those two articles to compare both bundlers - in both speed & ease of use.

Use cases

There are multiple reasons you would like to do it. You can have multiple single-page apps in one repository and share the build setup to speed things up. Another reason would be to make sure the build creates chunks that are reusable between apps. Or, you have a legacy HTML+JS application with many HTML files & you want to keep separate JS for each of them.

Code

First, we have 2 simple HTML files on the top level of the project:

a.html:

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>A part of our website</title>
    <link rel="shortcut icon" href="#" />

    <div id="view"></div>

    <script type="module" src="./dist/a.js"></script>
  </head>
  <body>
    <h1>Page A</h1>

    <p id="core"></p>

    <p id="a">(a placeholder)</p>
    <p id="b">(b placeholder)</p>

    <p><a href="b.html">Go elsewhere</a></p>
  </body>
</html>

b.html:

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>B part of our website</title>
    <link rel="shortcut icon" href="#" />

    <div id="view"></div>

    <script type="module" src="./dist/b.js"></script>
  </head>
  <body>
    <h1>Page B</h1>

    <p id="core"></p>

    <p id="a">(a placeholder)</p>
    <p id="b">(b placeholder)</p>

    <p><a href="a.html">Go elsewhere</a></p>
  </body>
</html>

Each of the files imports its JS file, and as we are using ESM output, we need to import it with <script type="module" .... All paragraphs <p id="... are meant to be updated by our JS & to let us see if all works as expected.

JavaScript

We have 3 simple files for testing our setup. Each file is using jQuery, so we can see if the chunks are created as expected - with the big library included in the shared file, and individual files small.

Besides that, the files are rather trivial

./src/core.js:

import $ from "jquery";

$("#core").html("Core module");

./src/a.js:

import $ from "jquery";
import "./core";

$("#a").html("A file, file A");

./src/b.js:

import $ from "jquery";
import "./core";

$("#b").html("B yourself");

Dependencies

Before installing the dependencies, let's first create an npm package:

$ npm init -y
Wrote to /home/marcin/workspace/github/esbuild-multipage/package.json:                                                                                                                 

{                                                                                                                                                                                      
  "name": "esbuild-multipage",                                                                                                                                                         
  "version": "1.0.0",                                                                                                                                                                  
  "description": "Example repo for an article about multipage with esbuild",                                                                                                           
  "main": "index.js",
...

Then you can install your dependencies with:

$ npm install --save jquery esbuild

added 2 packages, and audited 3 packages in 826ms

found 0 vulnerabilities

Build script

The build script is similar to what I used in the lazy loading example. If your configuration becomes more complicated, at some point will make sense to move it to a build script - as I show here.

To have your build available with npm run build, add to package.json:

{
  ...
  "scripts": {
    ...
    "build": "esbuild src/a.js src/b.js --bundle --outdir=dist --splitting --format=esm"
  }
  ...

The values in the command:

  • src/a.js & src/b.s - the entry points of the application
  • --bundle - bundle mode of esbuild
  • --outdir=dist - the folder where all the built code goes
  • --splitting - we turn on the experimental splitting behavior
  • --format=esm - another requirement of splitting to work - as of now, it's only working with es-modules output

Splitting

Splitting - making shared chunks for our entry points to share - is still work-in-progress. You can read more about it in (the documentation)[esbuild.github.io/api/#splitting]. If you get into trouble because of it, just remove --splitting from the build command.

Running build

So, how is our code doing in the end:

$ npm run build

> esbuild-multipage@1.0.0 build
> esbuild src/a.js src/b.js --bundle --outdir=dist --splitting --format=esm


  dist/chunk-TTCANJWN.js  226.2kb
  dist/a.js                 190b 
  dist/b.js                 186b 

⚡ Done in 23ms

Pretty sweet I would say!

Video course

You can check out my course about esbuild.

Summary

In this article, we have seen how to build a multipage website with esbuild. Stay tuned for other esbuild & javascript articles.