Skip to content

Is Bun the Next Big Thing in JavaScript? The 10+ Tools It Replaces

Published: at 01:40 PM

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 logo

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:

bun install benchmark

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.

bun test benchmark

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:

  1. Password hashing with Bcrypt or Argon 2 using Bun.password.hash and Bun.password.verify
  2. Replacement for semver
  3. Replacement for your any of the npm glob libraries
  4. A JSX-capable file system router similar to Astro
  5. Bun.deepEquals() provides a fast alternative to lodash or other libraries
  6. Easy HTTP and Socket server using Bun.serve including support for WebSockets and raw TCP Sockets. And it is fast:

Bun WebSocket Benchmark

Novel tools

Beyond just replacing many JavaScript ecosystem tools, it also adds some novel tools including:

  1. Runtime plugins
  2. Advanced configuration through bunfig.toml
  3. Import compile-time functions as macros using with { type: 'macro' }
  4. Project templating with bun create
  5. Foreign function interface to run code from Zig, Rust, C/C++, C#, Nim, Kotlin and more.
  6. HTML Rewriter with a Zig implementation of Cloudflare’s HTMLRewriter object.

Other things I love

  1. Bun provides useful information on import.meta such as import.meta.dir and import.meta.file. No need for weird ESM snippets to obtain CJS’s __dirname and __filename globals.
  2. 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:

  1. 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.
  2. 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.
  3. 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.