As your 11ty application evolves, you’ll want more organization with your JavaScript. Here’s a method for bundling together using esbuild.
As your 11ty application grows in size and complexity, it's very likely that:
Otherwise, you'll end up with unmanageable spaghetti code and slow pages. No one wants that, right?
To help stay organized, you can use tooling to take all the JavaScript in your application and bundle it into a single file that can be loaded from the pages that require it.
For this post, we're going to use esbuild, a JS bundler that is known for its speed.
Let's walk through the process of establishing a JS build pipeline for Eleventy using esbuild.
First, add esbuild to your project.
npm install -D esbuild
Next, make sure you have a main JavaScript file. For this example, I'll consider that file to be js/index.js
. It be whatever you like, but be sure to have an entry point.
js/index.js
// Your main JavaScript content here ...
Find more information on what you might include and how you may structure this file farther down in the post.
Next, we want to run the build whenever the 11ty build runs.
.eleventy.js
const esbuild = require("esbuild");
module.exports = function (eleventyConfig) {
eleventyConfig.on("eleventy.before", async () => {
await esbuild.build({
entryPoints: ["js/index.js"],
bundle: true,
outfile: "_site/js/bundle.js",
sourcemap: true,
target: ["chrome58", "firefox57", "safari11", "edge16"],
});
});
};
Notice that this targets our js/index.js
file, but bundles it into a file in the site output at _site/js/bundle.js
. Change the values here to fit your preferences.
And last, make sure that you're loading your bundle file from your layout or page(s) needing to use the bundle.
<!DOCTYPE html>
<html lang="en">
<!-- ... -->
<body>
<!-- ... -->
<script src="/js/bundle.js"></script>
</body>
</html>
That should be enough to get it working for a super simple site. See below for tips, tricks, and a demo.
Here are a collection of tips that I have found helpful while building out a similar approach for my sites.
You can configure your own watch targets that will tell 11ty when to rebuild the site. This is super helpful because it will tell 11ty to rebuild when you've edited a JavaScript file, thus giving you live reloading in the browser while developing.
.eleventy.js
module.exports = function (eleventyConfig) {
// ...
eleventyConfig.addWatchTarget("./js/");
};
Your main JavaScript file can import and export content as needed. That's the beauty of working with a JS bundler. This can help keep your JS files small and focused. Be sure to take advantage of this.
import something from "some-lib";
something;
But note that you're not going to get much out of your main export because it will be scoped within the bundle. More on this in the next tip.
esbuild works to minimize the size of your bundle. If you want something included in the bundle, it has to be used in the main file. It can't just be imported. Otherwise, it may be ignored.
The example above does this by simply listing the variable something
in the main body of the file.
You may want access to items within the bundle from pages in your application. The way I've handled this is to make them accessible through the globalThis
object (equivalent to window
in the browser).
I tend to import everything I want in my application into my main manifest file (js/index.js
), and then load those imports into a global App
object that is available on the window
.
import { Button } from "./components/button";
import { Slider } from "./components/Slider";
globalThis.App = {
components: { Button, Slider },
};
This pattern has two benefits:
window.App
) on any page that loads the bundle.Likewise, if you need access to global libraries directly in your pages, you also want to attach those to globalThis
in your main file.
import _ from "lodash";
globalThis._ = _;
This is not something I've needed to do, as I only use third-party libraries within other JavaScript files. Thus, I can import and use them directly without exposing them globally.
While bundling is convenient and a great way to stay organized, it also means that you may be requiring users to download code on pages that don't use it.
This is okay in small quantities. But as you include more third-party libraries and capabilities throughout your application, it's important to be cognizant of what you're adding to the page load as your JavaScript grows.
Here are a couple strategies for keeping a pulse on performance:
In short, 11ty provides you the flexibility to load whatever you want on any page. There's a lot of responsibility with that control. Use it wisely.
Here's a super simple example that sets up an esbuild pipeline and shows some content being replaced on screen.