Table of contents
- ES Modules
- Introducing esbuild
- Incremental compilation
- Watch mode
- Serve mode
By now you may have heard of esbuild -- but what the hell is it?
esbuild is an exciting new technology, I say as someone who has been building on esbuild for months. As Evan W. describes it, the creator of esbuild and co-founder of Figma, esbuild is a linker for the web. But what does that actually mean, and why should you care?
The benefits of a compiler are generally a) performing static type-checking at build-time and b) building to one binary or one binary per operating system. This is simple and elegant and easy to reason about, although perhaps makes it harder to debug runtime errors.
<script> tag. This is the happy path for one-off scripts, but what about for the case when you have many files and or dependencies?
<script type="module"> tag instead of
<script>. This is a step up over
<script> but is far from complete.
This is why linkers are important. A linker is a class of tooling that ‘links’ your code for you so you can worry about the thing you actually care about. Without a linker, you would need to do all the work of stitching your code together in a way that you and your browser, and your users’ browsers, can understand. But we can do better than that! We can use tools like webpack, Rollup, Parcel, or esbuild to automate resolving dependencies at build-time.
So why emphasize esbuild -- what’s new or different about esbuild?
esbuild has exhaustive documentation, a highly discoverable CLI experience, and is extremely fast. But what makes esbuild great is how well it solves for a well-defined, constrained problem space.
So what can you use esbuild for?
tsx) and CSS dependencies as deployable assets for the web. And you can do so with bundling or code-splitting, plugins, and more. And what makes esbuild such a quality-of-life tool for me personally is that it implements incremental compilation, watch mode, and serve mode.
There’s a lot to unpack there. I’ll iterate each point now:
- Bundling is for when you want to deploy a single
- Code-splitting is for when you want to code-split
app.jsinto many targets, such as
Header.js, etc. Note that this assumes support for ES Modules.
The plugin API allows you to preprocess files as they are linked. This is incredibly useful if you want to convert Markdown to HTML or JSX, Sass to CSS, etc. The plugin API defers these implementation details to you.
See the community repo for plugin ideas.
Incremental compilation means if you need to compile the same file repeatedly, say for example, as your sources change, you can do so without incurring a performance penalty. This is because esbuild only performs work on changed sources, rather than bundling or code-splitting from scratch every time.
Watch mode means esbuild can ‘pick up‘ changes to your source code as they occur. This means that you don’t need to worry about file-watchers or libraries like nodemon or chokidar; you can offload this work to esbuild and even implement your own watch handlers so you can observe events, log them, push server-sent events or WebSockets, etc.
Serve mode means you can use esbuild as a web server and implement your own serve handler for incoming requests, again to observe events, log them, etc. esbuild actually serves your bundled or code-split targets from memory, rather than from disk. This makes esbuild an incredibly performant web server because it reduces the amount of total work needed per request.
Now, let’s talk about caveats. 😱
esbuild is pre-1.0 software and not yet feature complete.
- My experience has demonstrated that this is not a cause for concern. The creator is incredibly mindful about what needs to change, why, and carefully documents every meaningful change in the changelog, even unreleased changes.
esbuild does not perform static type-checking.
- This is hardly a caveat since esbuild is a bundler, not a compiler in the conventional sense, but you can just as well plug
tscinto your build pipeline if static type-checking is important to you. (I don’t do this because I rely on VS Code alone for type-checking.)
- It’s worth mentioning that esbuild does log errors, warnings, and hints which can help you catch some errors, but these are generally syntactic errors.
esbuild is largely a one-man show.
- This can be interpreted as either a strength or a weakness, but I see this as a strength because esbuild as a technology is incredibly focused. It doesn’t try to solve every problem -- rather, a well-defined, constrained problem space that most frontend developers have or will experience.
- If you take a look at the issues, you’ll notice almost every issue has been addressed by community members or Evan, the creator. Development is pretty rapid and there’s generally a minor release once or twice a week.
esbuild is somewhat at odds with the Babel ecosystem.
- While you may want or need Babel for the latest and greatest CSS-in-JS library, I believe you can still implement whatever additional tooling you may need as a plugin. And if not, that may be a condition for when to not use esbuild.
esbuild supports many, many options.
- The main APIs are roughly only
serve, but the options these functions support are lengthy. Don’t worry if you don’t understand every option’s use-case; I don’t understand all of them.
- Note that esbuild’s CLI logger is extremely helpful. You can get very far with experimentation alone. The logger is very helpful about communicating when an option needs to be enabled and why.
esbuild is not designed for HMR.
- HMR stands for hot module replacement, which means state changes are persisted between browser refreshes. This may be a dealbreaker for you, if you’ve grown to love HMR.
- Personally speaking, I don’t love HMR because it makes reasoning about state more opaque. That being said, implementing fast-refresh with server-sent events or WebSockets is incredibly easy and fun with your own watch mode handler.
esbuild is not necessarily ‘for everyone‘.
- If you’ve read this far, experimenting with esbuild is probably for you. 😉 That being said, even if you’re not interested in esbuild, you’ve probably used it indirectly or will. Why? Because esbuild already powers Snowpack, Vite, SvelteKit, Remix Run, and more. There are hundreds if not thousands of tools by now that have or will implement esbuild as part of their build pipeline. Winter is coming.
esbuild is one of those rare technologies that deserves your time and attention. It is already helping to reimagine our industry and I’m incredibly excited to bet on it. It doesn’t feel like other tooling and is here to stay.
Top comments (4)
I didn't know esbuild until reading this article. After reading, I immediately proceeded to test it with a real-world production application. This application uses Gulp as its build tool, and Rollup to transpile Typescript. I've simply switched the gulp rollup plugin to the esbuild plugin and my total build time - which includes a lot of stuff like sass and other tools - dropped from 10~12 seconds to 5 seconds. Hot reloading after a change on any Typescript file dropped from 4 seconds to 1.2 seconds and this is due to eslint still being run under node, because transpiling time is now only taking less than half a second. Oh, and most importantly: the application still works with zero code change!
Thanks for this write up, esbuild is awesome! It's blazing fast because under the hood it uses WebAsembly. I've been impressed with its performance.
esbuild uses Go and concurrent algorithms which result in it being a performant tool. While it does support WASM as a host environment, that is not correlated (AFAIK) to esbuild being fast. It’s fast because it only uses V8 / node for resolving plugins.
It’s much easier to beat V8 / node performance metrics if you are building on Go, etc. and leveraging concurrency. As it turns out, most tools in the node ecosystem are built on top of node and therefore V8 which means performance will be hindered and concurrency will be hard to reason about.
I used ESbuild without knowing it, because it is incorporated in Hugo.