DEV Community

Marcin Wosinek
Marcin Wosinek

Posted on • Edited on • Originally published at how-to.dev

3 2

How to lazy load with esbuild

In this article, I will show you how to lazy load with esbuild. It's achieved by using a work-in-progress flag --splitting, so you may want to check out the documentation before you will start building something very complex with it.

Lazy loading

Is a pattern of delaying the download of a resource until it's needed. A common approach in web applications is to split critical & non-critical code into different files. In this way, non-critical code can be lazy-loaded in the background, while the user has already access to most of the features of the app.

The example

Similar to what I used in the webpack example, here we will have a simple js application, that happens to depend on a big, 3rd party library. The library I use, PDF-LIB was already discussed in an earlier post. PDF creation is a complex task, which requires a lot of code. Let's imagine an invoice application - one that allows for creating invoices & generating PDFs. it's an important feature of an application, but only called from some route & even there not needed immediately.

The code

For the example application, I have few files. index.html:

<!-- index.html -->
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Lazy load in esbuild</title>
    <link rel="shortcut icon" href="#" />

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

    <script type="module" src="./dist/index.js"></script>
  </head>
  <body></body>
</html>
Enter fullscreen mode Exit fullscreen mode

Simple loading the builts JS from the dist folder.

src/index.js:

const view = document.getElementById("view");

view.innerHTML = `<button id="pdf-button">Generate PDF</button>
<br>
<iframe id="pdf" style="width: 350px; height: 600px"></iframe>`;

import("pdf-lib").then(({ PDFDocument }) => {
  const pdfButton = document.getElementById("pdf-button");

  pdfButton.addEventListener("click", async () => {
    const pdfDoc = await PDFDocument.create();
    const page = pdfDoc.addPage([350, 400]);
    page.moveTo(110, 200);
    page.drawText("Hello World!");
    const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
    document.getElementById("pdf").src = pdfDataUri;
  });
});
Enter fullscreen mode Exit fullscreen mode

In this one file, we have 2 sections that will be executed in a different moments. The first 2 lines are run immediately after loading the js. They have our critical path - they set up the view for the user to interact with, while we load the rest of JS. The other is the callback for the dynamic import of pdf-lib. You can read more about dynamic imports on mdn, but in short, they are a part of the es-module specification. In short - it's loading another file during the runtime, and resolving a promise when it's available.

For the best user experience, you could set the Generate PDF button inactive here, and turn it active after PDF-LIB is available. For the sake of simplicity of the example code, I left the button unresponsive while the library loads.

Dependencies

After initializing your package with:

$ npm init -y
Enter fullscreen mode Exit fullscreen mode

you can install all dependencies with:

$ npm install --save esbuild pdf-lib
Enter fullscreen mode Exit fullscreen mode

Build code

You can add the build CLI command as an npm script to package.json:

{
  ...
  "scripts": {
    ...
    "build": "esbuild src/index.js --bundle --outdir=dist --splitting --format=esm"
  }
...
Enter fullscreen mode Exit fullscreen mode

The values we have here:

  • src/index.js - the entry point of the application
  • --bundle - we tell the esbuild to bundle the whole application
  • --outdir=dist - because of using splitting, just specifying the output file with --outfile is not enough - esbuild needs directory to put all chunks it creates there
  • --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

Links

Summary

After all this, our application will lazy load the big 3rd party dependency:

esbuild-lazy-load

If you want to see it in action yourself, the application is available here, and the source code:

GitHub logo marcin-wosinek / esbuild-lazy-load

Example repo for lazy load with esbuild

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay