Bun 1.0: An All-in-One JavaScript Runtime

On September 8, 2023, Jarred Sumner shipped Bun 1.0. It had been in development since 2021, building hype through benchmark screenshots and a growing Discord community. The pitch was bold: replace Node.js, npm, webpack, and Jest with a single binary. Runtime, bundler, package manager, and test runner, all in one tool, all absurdly fast.
I've been testing it for the past week across a few different project types. Here's my honest assessment.
What Makes Bun Different
Most JavaScript runtimes are built on V8 (Chrome's engine). Bun uses JavaScriptCore, the engine that powers Safari. Jarred chose JSC because of its faster startup time. The runtime itself is written in Zig, a systems language that gives similar performance characteristics to C and Rust but with a focus on simplicity.
The "all-in-one" part is what really sets Bun apart. Node.js is just a runtime. You need separate tools for everything else: npm or pnpm or yarn for packages, webpack or esbuild or Vite for bundling, Jest or Vitest for testing, ts-node or tsx for running TypeScript. Bun handles all of those natively.
Package Management Speed
This is where Bun's speed advantage is most dramatic. Installing a fresh Next.js project's dependencies:
- npm install: 38 seconds
- pnpm install: 14 seconds
- bun install: 2.8 seconds
That's not a typo. Bun's package manager is roughly 5x faster than pnpm and 13x faster than npm. It achieves this through aggressive caching, a binary lockfile (`bun.lockb`), and parallel resolution written in native code rather than JavaScript.
The lockfile format is important to mention. It's a binary file, not human-readable like `package-lock.json` or `pnpm-lock.yaml`. This is a trade-off: you gain speed but lose the ability to review lockfile diffs in code review. For teams that care about auditing dependency changes, this is a real downside.
Runtime Performance
Bun's runtime startup time is significantly faster than Node's:
# Time to run a simple script
# Node.js 20
time node hello.js # ~40ms
# Bun 1.0
time bun hello.js # ~8ms
For HTTP servers, the gap is also significant. A simple `Bun.serve()` handler handles roughly 2x to 3x the requests per second compared to Node's http module for basic workloads. The gap narrows when you add real application logic (database queries, template rendering), but Bun still tends to be faster.
Here's the built-in HTTP server API:
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/api/hello') {
return Response.json({ message: 'Hello from Bun' });
}
return new Response('Not found', { status: 404 });
},
});
The API is clean. It uses the Web Standard `Request` and `Response` objects, which means code written for Bun's server is portable to Cloudflare Workers, Deno, and any other platform that implements the Fetch API standard.
TypeScript and JSX: Zero Config
Bun runs TypeScript and JSX files directly. No `ts-node`, no build step, no `tsconfig.json` required (though it respects one if it exists). Just:
bun run app.tsx
It uses its own transpiler to strip types at runtime, similar to how `tsx` or `ts-node` with `--swc` works. Type checking still requires running `tsc` separately, since Bun's transpiler only strips types without validating them. This is the same trade-off that esbuild and SWC make: speed over type safety at build time.
The Test Runner
`bun:test` is a built-in test runner with a Jest-compatible API:
import { describe, expect, it } from 'bun:test';
describe('math', () => {
it('adds numbers', () => {
expect(2 + 2).toBe(4);
});
it('handles async', async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
});
It supports `beforeEach`, `afterEach`, `beforeAll`, `afterAll`, snapshot testing, and most of the matchers you're used to from Jest. The speed difference is noticeable: a test suite that takes 8 seconds in Jest typically runs in under 2 seconds with `bun:test`.
What's missing? Mocking is more limited. Jest's `jest.mock()` with automatic module mocking is more mature. Code coverage support landed recently but is still catching up to Jest and Vitest in terms of reporting options.
Node.js Compatibility
This is where the rubber meets the road. Bun aims for high compatibility with the Node.js API. It implements `fs`, `path`, `http`, `crypto`, `child_process`, `Buffer`, `process`, and most of the core modules. For many projects, you can run `bun run` where you'd normally run `node` and things just work.
But "most" isn't "all." I hit compatibility issues with a few things:
- Some native Node addons (anything using node-gyp) don't work. Bun has its own FFI system instead.
- Edge cases in the `http` module. Express works, but some middleware that digs into Node internals can break.
- `worker_threads` behavior has some differences.
- Some npm packages that rely on Node-specific internals fail silently.
The practical reality: simple Express or Fastify apps usually work. Complex applications with deep Node dependencies need testing. Always run your full test suite under Bun before committing to a migration.
Setting Up a Project
Starting a new project with Bun is refreshingly simple:
# Create a new project
bun init
# Install dependencies
bun add express
bun add -d typescript @types/express
# Run your app
bun run index.ts
# Run tests
bun test
# Bundle for production
bun build ./src/index.ts --outdir ./dist --target node
The `bun init` command scaffolds a minimal project with a `tsconfig.json` and entry file. The bundler (`bun build`) is still young compared to esbuild or Rollup, but it handles basic bundling tasks well.
Where Bun Excels
Three areas where I'd reach for Bun today without hesitation:
- Scripting and tooling. For CLI scripts, automation tasks, and developer tools, Bun's fast startup and native TypeScript support make it ideal. Writing a quick script in TypeScript and running it with `bun run script.ts` is the best developer experience I've found for this use case.
- New API servers. If you're building a fresh API without heavy native dependencies, Bun.serve() is excellent. The Web Standard APIs mean your code is portable.
- Package management. Even if you use Node as your runtime, `bun install` as your package manager is a free speed win. It generates a `node_modules` directory that works with Node, so you can mix and match.
Where Node Is Still Safer
For production applications with complex dependency trees, heavy native addon usage, or where you need battle-tested stability, Node.js 20 LTS is still the safer choice. Node's ecosystem compatibility is unmatched after 14 years. Its long-term support releases get security patches for years. The debugging tools (inspector protocol, flame graphs, heap snapshots) are mature.
Bun is version 1.0, and it shows. The pace of fixes and improvements is impressive, but you'll still hit edge cases that wouldn't exist in Node. For personal projects and greenfield services, that's fine. For production workloads handling real traffic, I'd recommend running Bun alongside your Node setup for a while before fully switching.
The Bigger Picture
Whether or not Bun "replaces" Node, it's already pushing the entire ecosystem forward. Node 20 and 21 shipped significant performance improvements partly because Bun set a new bar for what JavaScript runtime performance should look like. Deno is also iterating faster. Competition is making everything better.
I'm using Bun for all my scripting, most of my new side projects, and as a package manager across everything. For production services at scale, I'm watching closely but not rushing. That feels like the right balance for where Bun is today.