Introduction
Bundlers are an essential component of the JavaScript ecosystem. As the name implies, bundlers generate one or more module bundles. Module bundlers process the JavaScript applications and build dependency graphs to map each module needed by your project. Generally speaking, bundlers perform the following tasks:
- Bundle CSS, HTML, images, and other assets
- Bundle JavaScript code in required module formats
- Build optimizations, including code-splitting, scope-hoisting, tree-shaking, etc.
- Hot module replacement during the development
Many bundlers are used in developing modern JavaScript applications, including Webpack, Rollup, and parcel, but we will discuss a relatively new entrant, esbuild, which is a very fast and efficient bundler.
Steps we'll cover:
- Why another JS bundler
- Features of esbuild
- Comparison with other bundlers
- Why it is so fast
- Example esbuild Usage
- Is it Production ready?
- esbuild TypeScript support
Why another JS bundler
Technology is progressing very fast. Every day you will see new frameworks, build tools, and libraries to speed up and improve your software applications. esbuild is another example of constant innovation and improvement. It is an open-source next-generation JavaScript bundler that is very fast and more efficient than other bundlers in the industry. It is written in Go language with speed in mind; it is powered by parallel parsing, printing, and source map generation. It packages and bundles JS code for distribution on the web. Some of its features include:
- It is very fast, even without any cache. It is much faster than other bundlers.
- A robust API for JavaScript and Go
- ES6 and common JS modules
- Supports TypeScript and JSX syntax
- Source maps
- Minification
Features of esbuild
Let's go through some of its features in detail.
Bundling and supported content types
esbuild supports both bundling and code splitting. Bundling is when you want to deploy a single app.js
target. Code splitting is when you want to split app.js
into many targets, like header.js
or sidebar.js
etc.
esbuild has built-in support for various content types using its component called "loaders". The loader tells esbuild how to parse a particular content type. The three common loaders enabled by default are:
- Typescript loader
- JavaScript loader
- CSS loader
If we look at the content types supported by esbuild, then these are as below:
- JavaScript Loader: As mentioned above, the JavaScript loader is enabled by default, and it supports
.js
..cjs
, and.mjs
files - Typescript Loader: Enabled by default for
.ts
..tsx
.mts
, and.cts
files. However, it does not support type-checking. - JSX Loader: It is enabled by default for
.jsx
and.tsx
files. Note thatJSX
syntax is not enabled in.js
files by default. You can, however, enable this by updating the configuration. - JSON Loader: It is enabled by default for
.json
files. It parses JSON files to JavaScript objects and exports these objects. - CSS Loader: It can bundle CSS files directly without importing your CSS from the JavaScript code. This loader is also enabled by default.
- Text Loader: It is also enabled by default for
.txt
files. It loads the files as a string during build time and exports the string default export. - Binary Loader: It loads the file in the form of a binary buffer at build time and includes it in the bundle as Base64 encoding. It is not enabled by default.
- Data URL: It loads the file as a binary buffer at build time and embeds it into the bundle as a Base64 encoded data URL. This loader is useful for bundling images along with the CSS loader to load images using the method
url()
. It is not enabled by default.
The build API
esbuild has a powerful JavaScript build API through which you can customize the behavior of esbuild. It is similar to webpack.config.js
file in the Webpack.
If you look at the code sample below, you can see that the build function executes the esbuild in a child process and returns a promise that is resolved when the build is complete.
Note that esbuild also provides a synchronous build API buildSync
that runs synchronously. You will need to use the asynchronous build API because esbuild plugins are compatible with only asynchronous API.
require('esbuild').build({
entryPoints: ['app.jsx'],
bundle: true,
outfile: `bundle.js',
}).catch(() => process.exit(1))
Incremental compilation
esbuild supports incremental compilation. If you are compiling the same file from different sources again and again, esbuild will work only on changed sources instead of code splitting or bundling from scratch each time.
Plugins
The plugins API is a very useful feature of esbuild. It allows you to preprocess files when they are linked. It can be very beneficial if you are converting Sass to CSS or markdown to JSX etc. You can still configure the implementation details through the plugins API.
Server mode
The server mode enables you to use esbuild as a web server, and you can implement your own server handler for incoming requests. This feature is very powerful because you can use the server handler to perform different functions on incoming requests, like observe events and log them. esbuild utilizes code-split targets from memory instead of the disk to serve your bundled code, making it a highly performant web server as it reduces the total work spent on each request.
Watch mode
Watch mode means the esbuild can detect the changes in the source code as they occur. Instead of worrying about file-watchers or using libraries like Nodemon, or chokidar, etc. you can offload this responsibility to esbuild. In fact you can also implement your own watch handlers so you can log events, observe them and push server-sent events.
Comparison with other bundlers
If you look at the below comparison between different bundlers, you can see that esbuild has a significant performance advantage over its competitors. Image a large team with many projects and dependencies where reducing build times is crucial for product development. The magic lies in the ability of esbuild to parallelize printing, parsing, and source map generation.
Why it is so fast
Here is how esbuild is able to achieve this performance:
- It is developed using the Go language, which compiles to native code. Other bundlers are mostly written in JavaScript, and NodeJS has to spend extra time to parse the JavaScript in case of other bundlers.
- It is able to perform printing, parsing, and source map generation in parallel. The algorithms in esbuild are developed to fully saturate all CPU cores when possible. Note that parallelism is at the heart of Go language, and Go can make intelligent use of memory utilization as compared to JavaScript.
- Everything is done in very few passes instead of expensive data transformation.
- It has not too many features like Webpack, and its main focus is speed.
Example esbuild Usage
First, you need to create a NodeJS project by running this command
npm init –y
Go to your project directory and install the esbuild package by running the below command:
npm install esbuild
To verify if esbuild is correctly installed, run the below command, and it will return the esbuild version:
/node_modules/.bin/esbuild — version
This example uses using React application, so you need to run the following command to install react packages:
npm install react react-dom
Create an app.jsx
file having the following code:
import * as React from "react";
import * as Server from "react-dom/server";
let Greet = () => <h1>Hello, esbuild Users</h1>;
console.log(Server.renderToString(<Greet />));
Now let's ask esbuild to bundle this application by running the below command:
./node_modules/.bin/esbuild app.jsx — bundle — outfile=bundle.js
What esbuild does here is that it bundles your application into bundle.js
, and the whole process is extremely fast.
Is it Production ready?
esbuild is a great tool with a lot of potentials, however, it is still a small project maintained by a single person. There are not a lot of open-source contributions to this project, and its author is the only person maintaining it. While esbuild shows great performance compared to its counterparts, being a new entrant, you will not see many projects in production with esbuild yet. It is better to test it on a side project and push it to production after it goes well for your need.
esbuild TypeScript support
For TypeScript-based projects in production, you can take advantage of using tsup.
Using tsup you can build your TypeScript applications with minimal configuration.
It uses esbuild behind the scene so you get the power of esbuild along with the convenience of tsup. We, at Refine, have seen remarkable performance using tsup in our project dev/build processes.
Conclusion
The world of JavaScript has a lot of great frameworks and tools. You will see many bundlers in the market, but esbuild is gaining a lot of popularity due to its amazing speed. In this article, we compared some top bundlers being used. We also discussed some of the core features of esbuild and how it delivers blazing-fast builds.
We also went through some basic commands for installing and building projects with esbuild. esbuild has a lot of future, and although it is a new kid on the block, it holds tremendous potential for organizations that want to build applications quicker and faster.