However, sometimes I want a little more control and speed in my bundling process. That is why I decided to test a new and exciting bundler called esbuild. To do this, I convert a project I’d built with CRA to a custom version that uses esbuild. As an introduction to esbuild, CRA users may find this particularly useful.
What is esbuild?
Esbuild’s claim to fame is that it’s fast. This is the driving force behind using it, though there are lots of other cool things it can do (see the “major features” on the first page of the docs).
Why use esbuild?
So you can build faster! There are roughly two sides to building an app, development and production.
During development, esbuild uses incremental builds, which make it very easy to change your code, save, and see your updated app. This means you don’t have to rebuild your entire application every time you make a change during development.
Second, there is the production side to building a website. Bundling for production is often different from bundling for development, because you’ll usually want to do things like minify your source code, tree shake your app, and create source maps. You don’t really need to do these things during development, as these features add more time to building your bundle. For small projects that aren’t rebuilt often, the time it takes to build the bundle isn’t that big of a deal.
However, for larger projects you typically require CI/CD pipelines to catch errors before pushing to production, as well as preview deployments created on every commit. Because of features like these, your production build is generated more often than you’d expect.
To speed up this process, developers need to get feedback on whether their build passes the test suite, so they can share preview deployments with reviewers more quickly. With it’s incredible speed, esbuild tightens the feedback loop for developers, leading to faster insights and thus new features getting shipped more quickly.
Currently this application uses Create React App. We are going to replace CRA with esbuild.
We begin the process by installing esbuild and uninstalling react-scripts.
Next we need to update our public/index.html file to fix how CRA handled some images. Since esbuild does not bundle css the same way as CRA, we also need to import all our styles generated by Tailwind CSS in this file. If you’re not familiar with Tailwind, don’t worry about it, just know that it’s been set up to automatically generate build/index.css which has all of the styles we use.
Bundling an application with esbuild
Now we’re ready to bundle our application with esbuild. If we run the following command in the terminal, it will create the production build almost instantly:
We use an IIFE (Immediately Invoked Function Expression) to call esbuild directly, passing in many of the same commands as we passed to the CLI. There is much more you can do with esbuild, but this is a nice start.
Let’s go ahead and update the scripts section in package.json.
Now we run npm run build to make Tailwind create the production stylesheet and esbuild generate the production bundle. If we run npx serve -s public, we can start hosting a local version of the application.
Comparing esbuild and CRA build speeds
If you ran the CRA build before, you’ll notice that esbuild is significantly faster.
I compared running the two side by side and the build time was reduced by 70% using esbuild. The majority of the esbuild version’s runtime is running Tailwind CSS/PostCSS, so the actual work esbuildis doing takes almost no time. This is a really small app, so your mileage will vary, but as your application gets larger, esbuild will scale really well.
Implementing live reloading
We’ve gained some speed, but we’ve lost a big feature, the development version of our application. As a modern web developer, I want to see the changes I make in my code reflect nearly instantly in the browser, which is something CRA gave us for free.\
To get live-reloading setup in esbuild, we’re going to need to do a little bit of work. First, let’s start by installing 2 helpful tools, chokidar and live-server:
Next, we’ll add a start.js file in the scripts directory we created earlier:
There’s a little more going on here, but hopefully the code is pretty easy to follow. Basically we’re using chokidar to watch if we make any changes inside of the src directory, and if we do it will rebuild the application. For more details, take a look at the more fleshed out example from which I derived my script. Now we can run npm start and have our application automatically reload when we make changes to our codebase. Take a look at the code changes we made. It really didn’t take a whole lot to replace CRA with esbuild (especially if you ignore the package-lock.json changes 😉).
What we gained
I can’t stress speed enough, this is esbuild’s biggest draw. It’s freaking fast!
We also gained a lot of control. Don’t get me wrong, abstracting things away and using pre-existing frameworks and tools to get your apps up and running can be incredibly valuable. But sometimes it’s nice, and even necessary, to look under the hood and be able to hook things up for yourself.
Additionally, esbuild does not have a 1.0 version yet. People can and certainly have used esbuild in production settings, however it is still under very active development, so approach with caution when deciding if it’s the right tool for your next project.
There are a number of community plugins available which handle things like converting markdown, implementing various css flavors, compiling Svelte or Vue components, and much more. You can create your own plugins if you need, but it’s definitely worth checking out the community plugins first.
If this blog has made you excited about the potential that esbuild unlocks for frontend development, definitely check out Snowpack (a frontend build tool) or Remix (a React framework) which are both amazing and use esbuild. For further inspiration, checkout the thousands of open source projects using esbuild.