Bun vs Node.js: Everything you need to know
Tags: javascript, node, bunjs, tutorial
Published: 2023-09-21 15:01:06

_Written by [Vishwas Gopinath](https://twitter.com/CodevolutionWeb)_

On September 8th, there was fresh buzz in the JavaScript community: Bun v1.0, created by [Jarred Sumner](https://twitter.com/jarredsumner), had arrived. But with all the talk, many are left wondering: What's the essence of Bun? Why is everyone drawing parallels with the tried-and-true [Node.js](https://nodejs.org/en)? Is Bun just another fleeting trend, or is it here to redefine the game? In this article, let’s dive deep into Bun, check out its features, and find out how it compares to the well-established Node.js.

## **What is Bun?**

Bun is a super fast all-in-one toolkit for JavaScript and TypeScript apps. The beauty of Bun lies in its ability to streamline the development process, making it smoother and more efficient than ever. This is possible because Bun is not just a runtime, it's also a package manager, a bundler, and a test runner. Imagine having a Swiss Army knife for JS development; that's Bun for you.

## **What Bun solves for**

The inception of Node.js in 2009 was groundbreaking. However, as with many technologies, as it grew, so did its complexity. Think of it like a city. As a city expands, traffic congestion can become a problem.

Bun aims to be the new infrastructure that alleviates this congestion, making things run smoother and faster. It's not about reinventing the wheel but refining it, ensuring that while we get the speed and simplicity, we don't lose the essence of what makes JavaScript unique and powerful.

Bun is designed as a faster, leaner, more modern replacement for Node.js so let’s take a closer look at some comparison. But first let’s discuss one other topic.

### **Node.js versus Deno versus Bun**

When discussing the evolution of JavaScript runtimes, it's hard to overlook [Deno](https://deno.com/). Ryan Dahl, the creator of Node.js, introduced Deno as a new runtime that aimed to address some of the challenges and regrets he identified with Node.js.

Deno is a secure runtime for JavaScript and TypeScript. It directly addresses many of the shortcomings of Node.js. For instance, Deno natively supports TypeScript without the need for external tools. Unlike Node.js, where scripts have broad permissions by default, Deno adopts a security-first approach requiring developers to explicitly grant permissions for potentially sensitive operations, such as file system access or network connectivity.

While Deno presents a compelling alternative to Node.js, it hasn't matched Node.js's widespread adoption. Therefore, this article centers on contrasting Bun with the well-established Node.js.

## **Getting started**

With Bun, we can scaffold an empty project with the command `bun init -y`. We have a few files generated and in index.ts, add a line, `console.log("Hello, Bun!")`. In the terminal, run the command `bun index.ts` to see “Hello, Bun!” logged.

## **Bun versus Node.js: JavaScript runtime**

A JavaScript runtime is an environment which provides all the necessary components in order to use and run a JavaScript program.

Both Node.js and Bun are runtimes. Node.js is primarily written in C++ where as Bun is written with a low-level general purpose programming language called [Zig](https://ziglang.org/). But that is just the tip of the ice berg. Let’s take a closer look at other differences when treating Bun as a runtime alone.

### **JavaScript engine**

A JavaScript engine is a program that converts JavaScript code we write into machine code that allows a computer to perform specific tasks.

While Node.js uses Google's [V8 engine](https://v8.dev/) that power's Chrome browser, Bun uses [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore) (JSC), which is an open source JavaScript engine developed by Apple for Safari.

V8 and JSC have different architectures and optimization strategies. JSC prioritizes faster start times and reduced memory usage with a slightly slower execution time. On the other hand, V8 prioritizes fast execution with more runtime optimization which may lead to more memory usage.

This makes Bun fast, starting up to 4x faster than Node.js.

 ![Summary: bun ran 2.19 times faster than deno and 4.81 times faster than node](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fffa01b170c5e450e9a9f8e1e347e39a6?width=742)

Summary: bun ran 2.19 times faster than deno and 4.81 times faster than node

### **Transpiler**

While Node.js is a powerful runtime for JavaScript, it doesn't natively support TypeScript files. To execute TypeScript in a Node.js environment, external dependencies are required. One common approach is to use a build step to transpile TypeScript (TS) into JavaScript (JS) and then run the resulting JS code. Here's a basic setup that uses the `ts-node` package:

**1. Installation**

```bash
npm install -D typescript ts-node
```

**2. Script configuration**

In your `package.json`, you can set up scripts to streamline the process:

```js
{
  "scripts": {
    "start": "ts-node ./path/to/your/file.ts"
  }
}
```

**3. Execution**

With the above script, you can easily run your TypeScript file:

```bash
npm start
```

In contrast, Bun offers a more streamlined approach. It comes with a JavaScript transpiler integrated into the runtime. This allows you to directly run .`js, .ts, .jsx `and `.tsx` files. Bun's built-in transpiler seamlessly converts these files to vanilla JavaScript, facilitating immediate execution without additional steps.

```bash
bun index.ts
```

The difference in speed is magnified when running a TypeScript file as Node.js requires a transpilation step before it can be run.

 ![Bun takes 8ms, Node esbuild 40ms, tsx 120ms and tsx 350ms](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fa929240bcfb647f7b019075de9d6f9b9?width=742)
### **ESM and CommonJS compatibility**

A module system allows developers to organize code into reusable segments. In JavaScript, the two primary module systems are [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) and [ES modules](https://nodejs.org/api/esm.html#modules-ecmascript-modules) (ESM). CommonJS, originating from Node.js, uses `require` and `module.exports` for synchronous module handling, ideal for server-side operations.

ESM, introduced in ES6, employs `import` and `export` statements, providing a more static and asynchronous approach, optimized for browsers and modern build tools. Let's use `colors` for CommonJS and `chalk` for ESM, two popular packages for adding colored outputs to the console and to better understand the module systems.

Node.js has been traditionally associated with the CommonJS module system. Here's a typical usage:

```ts
// CommonJS in Node.js (index.js)
const colors = require("colors");
console.log(colors.green('Hello, world!'));
```

For ES modules in Node.js, you have one of two options:

1. You need to include `"type": "module"` in your `package.json`.
2. Use the `.mjs` extension.

```ts
// ESM in Node.js (index.mjs)
import chalk from 'chalk';
console.log(chalk.blue('Hello, world!'));
```

The transition from CommonJS to ES modules (ESM) has been a complex journey. It took Node.js half a decade post ESM's introduction to support it without the experimental flag. Still, CommonJS remains prevalent in the ecosystem.

Bun simplifies the module system by supporting both without any special configuration. Bun's standout feature is its ability to support both `import` and `require()` in the same file, something not natively possible in Node.js:

```ts
// Mixed modules in Bun (index.js)
import chalk from "chalk";
const colors = require("colors");

console.log(chalk.magenta('Hello from chalk!'));
console.log(colors.cyan('Hello from colors!'));
```

### **Web APIs**

Web APIs, integral to browser-based applications, offer tools like `fetch` and `WebSocket` for web interactions. While these have become browser standards, their support in server-side environments like Node.js has been inconsistent.

In earlier versions of Node.js, Web standard APIs commonly available in browsers weren't natively supported. Developers had to rely on third-party packages like `node-fetch` to replicate this functionality. However, from Node.js v18, there's experimental support for the `fetch` API, potentially eliminating the need for these packages.

Bun simplifies this by offering built-in support for these Web standard APIs. Developers can directly use stable `fetch`, `Request`, `Response`, `WebSocket` and other browser-like APIs without any additional packages. Furthermore, Bun's native implementation of these Web APIs ensures they are faster and more reliable compared to third-party alternatives.

Here's an example compatible with both Node.js (v18 and above) and Bun. While it's experimental in Node.js, the same functionality is stable in Bun:

```ts
// Experiment fetch in Node.js (v18 and above) and built-in fetch in Bun
async function fetchUserData() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
  const user = await response.json();
  console.log(user.name);
}

fetchUserData(); // Leanne Graham
```

### **Hot reloading**

Hot reloading is a feature that boosts developer productivity by automatically refreshing or reloading parts of an application in real-time as the code changes, without requiring a full restart.

In the Node.js ecosystem, you have a couple of options for achieving hot reloading. One popular tool has been `nodemon`, which hard-restarts the entire process:

```bash
nodemon index.js
```

Alternatively, starting from Node.js v18, there's an experimental `--watch` flag introduced:

```bash
node --watch index.js
```

Both methods aim to provide real-time reloading of the application as code changes. However, they might have different behaviors, especially in certain environments or scenarios.

For instance, `nodemon` can lead to disruptions like disconnecting HTTP and WebSocket connections, while the `--watch` flag, being experimental, might not offer the full suite of features and has some reported issues in the [GitHub issues](https://github.com/nodejs/node/issues?q=--watch).

Bun takes hot reloading a step further. By running Bun with the `--hot` flag, hot reloading is enabled:

```bash
bun --hot index.ts
```

Unlike the Node.js methods that might require a full process restart, Bun reloads your code in-place without terminating the old process. This ensures that HTTP and WebSocket connections remain uninterrupted and the application state is preserved, providing a smoother development experience.

### **Node.js compatibility**

When transitioning to a new runtime or environment, compatibility is often a primary concern for developers. Bun addresses this by positioning itself as a drop-in replacement for Node.js. This means that existing Node.js applications and npm packages can seamlessly integrate with Bun without any modifications. Key features that ensure this compatibility include:

- Support for built-in Node.js modules such as `fs`, `path`, and `net`.
- Recognition of global variables like `__dirname` and `process`.
- Adherence to the Node.js module resolution algorithm, including the familiar `node_modules` structure.

Bun is still evolving. It's tailored to enhance development workflows and is ideal for environments where resources are limited, such as serverless functions. The team behind Bun is striving for comprehensive Node.js compatibility and better integration with prevalent frameworks

While Bun ensures compatibility with Node.js, it doesn't stop there. Bun ships with highly-optimized, standard-library APIs for the things you need most as a developer.

### **Bun APIs**

**Bun.file()**

Lazily load files and access their content in various formats. This method is up to 10x faster than its Node.js counterpart.

```ts
// Bun (index.ts)
const file = Bun.file("package.json");
await file.text();

// Node.js (index.mjs)
const fs = require("fs/promises");
const fileContents = await fs.readFile("package.json", "utf-8");
```

**Bun.write()**

A versatile API to write data to disk, from strings to Blobs. It writes up to 3x faster than Node.js.

```ts
// Bun (index.ts)
await Bun.write("index.html", "<html/>");

// Node.js (index.mjs)
const fs = require("fs/promises");
await fs.writeFile("index.html", "<html/>");
```

**Bun.serve()**

Set up HTTP server or WebSocket server using Web-standard APIs. It can serve 4x more requests per second than Node.js and handle 5x more WebSocket messages than the `ws` package in Node.js. This backend capability is reminiscent of how developers use Express in Node.js but with the added benefits of Bun's performance optimizations.

```ts
// Bun (index.ts)
Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello from Bun!");
  },
});

// Node.js (index.mjs)
import http from "http";
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello from Node.js!");
});
server.listen(3000);
```

Bun also has support for sqlite and password built-in.

## **Bun versus Node.js: package manager**

Bun is more than just a runtime; it's an advanced toolkit that includes a powerful package manager. If you've ever found yourself patiently waiting during dependency installations, Bun offers a refreshingly faster alternative. Even if you don't use Bun as a runtime, its built-in package manager can speed up your development workflow.

Check out this table comparing Bun commands with [npm](https://www.npmjs.com/), Node's package manager:

At a glance, Bun's commands might seem familiar but the experience is anything but ordinary. Bun boasts installation speeds that are orders of magnitude faster than npm. It achieves this by leveraging a global module cache, eliminating redundant downloads from the npm registry. Additionally, Bun employs the fastest system calls available for each operating system, ensuring optimal performance.

Here's a speed comparison of installing dependencies for a starter Remix project from cache, comparing Bun and npm:

 ![Bun takes 0.36s, pnpm 6.44s, npm 10.58s and yarn 12.08s](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F68bfdc8b5baa41dba053822bca1ff1f7?width=742)

The `bun` CLI contains a Node.js-compatible package manager designed to be a dramatically faster replacement for `npm`, `yarn`, and `pnpm`

Moreover, a `bun run <command>` takes just 7ms, while `npm run <command>` takes 176ms. While Node.js's npm has been the standard for JavaScript package management for years, Bun really is a speed powerhouse and presents a compelling alternative.

## **Bun versus Node.js: bundler**

Bundling is the process of taking multiple JavaScript files and merging them into one or more optimized bundles. This process can also involve transformations, such as converting TypeScript to JavaScript or minifying the code to reduce its size.

In the Node.js ecosystem, bundling is typically handled by third-party tools rather than Node.js itself. Some of the most popular bundlers in the Node.js world include [Webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), and [Parcel](https://parceljs.org/), offering features like [code splitting](https://developer.mozilla.org/en-US/docs/Glossary/Code_splitting), [tree shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking), and [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/).

Bun, on the other hand, is not just a runtime and a package manager but also a bundler in its own right. It's designed to bundle JavaScript and TypeScript code for various platforms, including frontend applications in the browser (React or Next.js applications) and Node.js.

To bundle with Bun, you can use a simple command:

```bash
bun build ./index.ts --outdir ./build
```

This command bundles the `index.ts` file and outputs the result in the `./build` directory. The bundling process is incredibly fast, with Bun being 1.75x faster than esbuild, and significantly outpacing other bundlers like Parcel and Webpack.

 ![Bun takes 0.17s, esbuild 0.3s, rspack 4.45s, Parcel 2 26.32s, Rollup 32s and Webpack 5 38.02s](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fd43dc089058a4b448de3c189ed20cbf6?width=742)

Bun takes 0.17s, esbuild 0.3s, rspack 4.45s, Parcel 2 26.32s, Rollup 32s and Webpack 5 38.02s

A standout feature in Bun is its introduction of JavaScript macros. These allow for the execution of JavaScript functions during bundling, with the results directly inlined into the final bundle. This mechanism offers a fresh perspective on bundling.

Check out this example where Bun's JavaScript macros are leveraged to fetch a username during the bundling process. Instead of making a runtime API call, the macro fetches the data at bundle-time, inlining the result directly into the final output:

```ts
// users.ts

export async function getUsername() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
  const user = await response.json();
  return user.name;
}

// index.ts

import { getUsername } from "./users.ts" with { type: "macro" };
const username = await getUsername();

// build/index.js

var user = await "Leanne Graham";
console.log(user);
```

While Node.js has its established bundling tools, Bun offers an integrated, faster, and innovative alternative that could reshape the bundling landscape.

## **Bun versus Node.js: test runner**

Testing is a crucial aspect of software development, which ensures that code behaves as expected and catches potential issues before they reach production. In addition to being a runtime, a package manager and a bundler, Bun is also a test runner.

While Node.js developers have traditionally relied on [Jest](https://jestjs.io/) for their testing needs, Bun introduces a built-in test runner that promises speed, compatibility, and a range of features that cater to modern development workflows.

Bun's test runner, `bun:test`, is designed to be fully compatible with Jest, a testing framework known for its "expect"-style APIs. This compatibility ensures that developers familiar with Jest can easily transition to Bun without a steep learning curve.

```ts
import { test, expect } from "bun:test";

test("2 + 2", () => {
  expect(2 + 2).toBe(4);
});
```

Executing tests is straightforward with the `bun test` command. Moreover, Bun's runtime supports TypeScript and JSX out of the box, eliminating the need for additional configurations or plugins.

### **Migrating from Jest or Vitest**

Bun's commitment to compatibility shines through its support for Jest's global imports. For instance, importing from `@jest/globals` or `vitest` will be internally re-mapped to `bun:test`. This means that existing test suites can run on Bun without any code modifications.

```ts
// index.test.ts
import { test } from "@jest/globals";

describe("test suite", () => {
  test("addition", () => {
    expect(1 + 1).toBe(2);
  });
});
```

### **Performance benchmarks**

Bun's test runner is not just about compatibility; it's about speed. In a benchmark against the test suite for [Zod](https://zod.dev/), Bun proved to be 13x faster than Jest and 8x faster than Vitest. This speed advantage is further highlighted by Bun's matchers, which are implemented in fast native code. For instance, `expect().toEqual()` in Bun is a staggering 100x faster than Jest and 10x faster than Vitest.

Whether you're looking to migrate existing tests or start a new project, Bun provides a robust testing environment that aligns with modern development needs.

### **Conclusion**

Node.js has long been a cornerstone in the JavaScript world, setting benchmarks and guiding developers. However, Bun is stepping onto the scene as a noteworthy challenger, pushing boundaries.

While it's still early days for Bun, the buzz it's generating is undeniable. Currently, it's optimized for MacOS and Linux, and while Windows support is in progress, some features are still on the horizon. With all it offers, Bun is certainly a toolkit you should consider exploring.


 ![](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F83677fb912dd4a539a3f3bb5c0c82de1?width=405)
<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F77cc5249f71948568195e7314bcc5766%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=77cc5249f71948568195e7314bcc5766&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>

Visually build with your components

[Builder.io](https://www.builder.io/m/developers) is a visual editor that connects to any site or app and lets you [drag and drop](https://www.builder.io/m/products) with [your components](https://www.builder.io/m/developers).

[Try it out](https://builder.io/signup) [Learn more](/m/developers) 
```ts
// Dynamically render your components
export function MyPage({ json }) {
  return <BuilderComponent content={json} />
}

registerComponents([MyHero, MyProducts])
```

##### _Read the [full post](https://www.builder.io/blog/bun-vs-node-js) on the [Builder.io blog](https://www.builder.io/blog)_