Bun’s killer feature isn’t speed or TypeScript support or Node compatibility. It’s a brand new approach; a unified toolkit that allows us to move past the patchwork of tools that JavaScript Engineers must juggle today.
Bun 1.0 was released in September 2023. I’ve been using it on a production application since November 2023 and want to share what I’ve learned.
Bun calls itself “a fast all-in-one toolkit”. On the surface it is a replacement for Node.js and a competitor to Deno. Like Deno, it supports TypeScript without a compilation step. It’s faster than Node and Deno thanks to its use of Zig, a modern bare-metal programming language that is very fast.
Bun’s goal is to provide “complete Node.js API compatibility.” You can check out their progress of implementing Node APIs here. In my experience, Bun is fully compatible with most normal use cases. I am able to run a large Remix app on Bun without a hiccup.
But Bun is not just a runtime. It is a vision for a faster and more integrated JavaScript developer tooling ecosystem. Bun includes 10+ tools that replace a ton of the janky workarounds and slow userland code in the JavaScript ecosystem.
1. Package Manager
Bun includes a built-in package manager, eliminating the need for external package managers like npm, yarn, or pnpm.
Why is it faster? Bun runs on Zig and other package managers run on JavaScript.
How fast? On my machine bun is 10 times faster than npm ci
and 3 times faster
than npm i
.
Here is their self-reported benchmark performance:
One fun feature is that if you forget to run bun install
on your project
before running it, Bun will
auto-install your dependencies.
2. Watch Mode and Hot Reloading
Bun comes with a built-in watch mode and hot
reloading, replacing tools like pm2 or nodemon. In watch mode, bun monitors file
changes and automatically reloads your application. When using Bun.serve()
to
serve http and socket traffic, your code will update in place without closing
any connections.
3. Shell Wrapper for a Simplified package.json
Bun’s shell wrapper eliminates the need for multiple tools you see in package.json files like cross-env, rimraf, and others. Outside of package.json, it allows you to use common Linux commands with JavaScript objects such as buffers, streams, and fetch responses.
Example:
import { $ } from 'bun';
const buffer = Buffer.alloc(100);
const result = await $`echo "Hello World!" > ${buffer}`;
console.log(result.exitCode); // 0
console.log(buffer.toString()); // Hello World!
4. Test Runner
Bun includes a test runner that replaces vitest
or jest. With “bun test,” you get all the same features, but significantly
faster and without a series of giant dependencies. It includes support for
popular libraries for
testing DOM and React components using
happy-dom
and React Testing Library.
5. Coverage Analyzer
Included with Bun’s test runner is a coverage analyzer that is comparable to istanbul.
6. Bundling Made Easy
Bun includes a bundler that replaces the need for separate tools like Vite, ESBuild, or Turbopack. It supports TypeScript of course, but also comes with built-in support for JSX, no plugin needed. Like Vite, it also supports transformation plugins.
As a bonus, you can simply use the --compile
flag to build a
standalone executable.
7. Automatic .env File Reading
Bun automatically reads .env files, allowing
you to ditch dotenv
or alternatives. The idea is not novel as Node 21 also
introduces that feature.
8. Run Dependency Scripts with “bunx”
Instead of using “npx” to run dependency scripts, Bun provides a “bunx” command. It’s faster and equivalent.
9. Execution
Running script files written in TypeScript is a tricky prospect. In my experience, ts-node often gets into a catch-22 because tsconfig.json, package.json and dependencies must be all aligned with either CJS or ESM. Bun treats CJS and ESM interchangeably, so you never get stuck in what I like to call “CJS/ESM hell”. Let me emphasize this point: with Bun, you don’t have to choose CJS or ESM; they both interoperate seamlessly.
In some projects I’ve resorted to a bash script that compiles a script file with esbuild to /tmp and then runs it with Node. Using Bun is so refreshing. Everything just works together nicely.
10. REPL
I often use node
to spin up a REPL to test and see how things behave alone in
Node.js. Bun comes with bun repl
that allows you to do the same, but in
TypeScript. It is useful to try out Bun’s proprietary built-ins such as
Bun.file
and
Glob
.
11. SQLite
Like Deno, Bun decided to include native support for SQLite. You can run queries by importing the database file like a dependency:
import db from './my.db' with { type: 'sqlite' };
console.log(db.query('select * from users LIMIT 1').get());
Other npm packages
Bun includes several native replacements of commonly used npm packages:
- Password hashing with Bcrypt or Argon 2
using
Bun.password.hash
andBun.password.verify
- Replacement for
semver
- Replacement for your any of the npm glob libraries
- A JSX-capable file system router similar to Astro
Bun.deepEquals()
provides a fast alternative to lodash or other libraries- Easy HTTP and Socket server using Bun.serve including support for WebSockets and raw TCP Sockets. And it is fast:
Novel tools
Beyond just replacing many JavaScript ecosystem tools, it also adds some novel tools including:
- Runtime plugins
- Advanced configuration through bunfig.toml
- Import compile-time functions as macros
using
with { type: 'macro' }
- Project templating with
bun create
- Foreign function interface to run code from Zig, Rust, C/C++, C#, Nim, Kotlin and more.
- HTML Rewriter with a Zig
implementation of Cloudflare’s
HTMLRewriter
object.
Other things I love
- Bun provides useful information on
import.meta
such asimport.meta.dir
andimport.meta.file
. No need for weird ESM snippets to obtain CJS’s__dirname
and__filename
globals. - There is a suite of utility functions specifically made for converting binary data from one format to another. It eliminates the need for a variety of user-land packages and snippets you’ll find on Stack Overflow.
Rough edges
I’ve encountered some rough edges. Updates are released at least weekly and some bugs I’ve run into are resolved in a matter of weeks. Currently Bun is at version 1.0.26, so you can see the maintainers are very busy.
Here’s a list of rough edges:
- Sometimes it just exits. No error, just stops running. Usually the problem goes away if I fix the problem code or drop an offending dependency. For example, I’ve had a case where I accidentally treated a Promise like a value and it was hard to find the problem because Bun exited with no message.
- Sometimes it segfaults. No helpful information, just completely stops. One
case early on, a bug in
console.log()
caused segfaults when certain values were passed. - Some npm libraries just don’t work and it’s not easy to figure out why. For
example, I couldn’t get
probe-image-size
to work correctly. I migrated a large Remix app to Bun and it took a lot of work to find all the incompatible dependencies. But it does work now!
What I’ve learned running Bun in production
Bun has been stable and reliable. I have a project that uses Bun.serve
for a
WebSocket application and drops to the command line to call ffmpeg
. It doesn’t
have a lot of npm dependencies which shows that Bun by itself is reliable.
Would I recommend using it?
For new projects, absolutely! For existing projects, yes, just be prepared to troubleshoot some incompatibilities and find some new npm dependencies.