Skip to main content
Technology & EngineeringBun219 lines

Bun Fundamentals

Bun runtime overview: all-in-one JavaScript runtime, bundler, test runner, and package manager. Installation, project initialization, Node.js compatibility, performance characteristics, and guidance on when to choose Bun vs Node.

Quick Summary33 lines
You are an expert in Bun, the high-performance JavaScript and TypeScript runtime built on JavaScriptCore (Safari's engine) and written in Zig.

## Key Points

- CommonJS `require()` and ES modules `import` -- Bun supports both, even mixed in the same file
- `__dirname`, `__filename` -- work in both ESM and CJS (unlike Node where they are CJS-only)
- `process.env`, `process.argv`, `process.cwd()`, `process.exit()`
- Most npm packages work without modification
- Native Node.js addons (`.node` files built with node-gyp) -- Bun has partial support via its N-API implementation, but complex addons may fail
- `node:vm` -- partially implemented, some edge cases differ
- `node:inspector` and `node:v8` -- not available (different engine)
- Some `node:cluster` features
- Packages that rely on V8-specific behavior or internal Node.js APIs
1. **Startup time**: Bun starts 4-5x faster than Node.js because JavaScriptCore has faster initialization and Bun skips the V8 startup overhead
2. **Package installation**: `bun install` is 10-30x faster than npm because it uses a binary lockfile, hardlinks from a global cache, and parallel downloads
3. **File I/O**: Bun's `Bun.file()` and `Bun.write()` use optimized system calls (io_uring on Linux, kqueue on macOS)

## Quick Example

```bash
bun --version
# 1.2.x
```

```bash
# Set memory limit
BUN_JSC_MAX_HEAP_SIZE=4096 bun run server.ts

# Enable garbage collection logging
BUN_JSC_logGC=1 bun run app.ts
```
skilldb get bun-skills/Bun FundamentalsFull skill: 219 lines
Paste into your CLAUDE.md or agent config

Bun Fundamentals — The All-in-One JavaScript Toolkit

You are an expert in Bun, the high-performance JavaScript and TypeScript runtime built on JavaScriptCore (Safari's engine) and written in Zig.

What Bun Is

Bun is four tools in one binary: a JavaScript/TypeScript runtime, a package manager, a bundler, and a test runner. It aims to replace Node.js, npm, webpack, and Jest with a single fast tool. Bun natively executes TypeScript, JSX, and TSX files without a separate compilation step. It uses JavaScriptCore (the engine behind Safari) rather than V8, which gives it different performance characteristics -- generally faster startup and lower memory usage for many workloads.

Bun is not a thin wrapper around Node.js. It is a ground-up implementation of the JavaScript runtime with its own event loop, HTTP server, file I/O, and module resolution. It implements most of the Node.js API surface for compatibility, but the internals are entirely different.

Installation

# macOS and Linux (recommended)
curl -fsSL https://bun.sh/install | bash

# Homebrew
brew install oven-sh/bun/bun

# Windows (native support since Bun 1.1)
powershell -c "irm bun.sh/install.ps1 | iex"

# npm (works but defeats the purpose for most use cases)
npm install -g bun

# Docker
FROM oven/bun:1
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
CMD ["bun", "run", "start"]

Verify installation:

bun --version
# 1.2.x

Project Initialization

# Create a new project
bun init

# This creates:
# - package.json (with "type": "module" by default)
# - tsconfig.json (TypeScript configured out of the box)
# - index.ts (entry point)
# - README.md

# Create with specific options
bun init --yes  # accept all defaults

The generated tsconfig.json uses Bun's recommended settings including "moduleResolution": "bundler" and "types": ["bun-types"] for full API autocompletion.

Running Code

# Run a TypeScript file directly -- no tsc needed
bun run index.ts

# Run a JSX file
bun run app.tsx

# Run a script from package.json
bun run dev

# Use --watch for auto-restart on file changes
bun --watch run index.ts

# Use --hot for hot reloading (preserves state)
bun --hot run server.ts

# Run with environment file
bun --env-file=.env.local run index.ts

The difference between --watch and --hot: watch mode restarts the entire process on file changes (like nodemon). Hot mode reloads modules in-place without restarting, preserving global state and open connections. Use hot mode for HTTP servers during development; use watch mode for CLI tools and scripts.

Node.js Compatibility

Bun implements most Node.js built-in modules. Import them exactly as you would in Node:

import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { join } from "node:path";
import { EventEmitter } from "node:events";

// These all work as expected
const data = await readFile("config.json", "utf-8");

What Works

  • node:fs, node:path, node:os, node:crypto, node:url, node:util, node:events, node:stream, node:buffer, node:http, node:https, node:net, node:tls, node:child_process, node:worker_threads, node:assert, node:zlib, node:string_decoder, node:querystring, node:readline, node:dns
  • CommonJS require() and ES modules import -- Bun supports both, even mixed in the same file
  • __dirname, __filename -- work in both ESM and CJS (unlike Node where they are CJS-only)
  • process.env, process.argv, process.cwd(), process.exit()
  • Most npm packages work without modification

What May Not Work

  • Native Node.js addons (.node files built with node-gyp) -- Bun has partial support via its N-API implementation, but complex addons may fail
  • node:vm -- partially implemented, some edge cases differ
  • node:inspector and node:v8 -- not available (different engine)
  • Some node:cluster features
  • Packages that rely on V8-specific behavior or internal Node.js APIs
// Check if running in Bun
if (typeof Bun !== "undefined") {
  console.log("Running in Bun", Bun.version);
} else {
  console.log("Running in Node.js", process.version);
}

Performance Characteristics

Bun's performance advantages come from several architectural decisions:

  1. Startup time: Bun starts 4-5x faster than Node.js because JavaScriptCore has faster initialization and Bun skips the V8 startup overhead
  2. Package installation: bun install is 10-30x faster than npm because it uses a binary lockfile, hardlinks from a global cache, and parallel downloads
  3. File I/O: Bun's Bun.file() and Bun.write() use optimized system calls (io_uring on Linux, kqueue on macOS)
  4. HTTP server: Bun.serve() handles more requests per second than Node's http.createServer for most workloads
  5. TypeScript: No transpilation step -- Bun strips types at parse time rather than running tsc or swc first

Where Node.js may still win:

  • Long-running computation-heavy tasks where V8's JIT optimizer has time to fully optimize hot paths
  • Workloads that depend on V8's highly-tuned garbage collector for large heaps
  • Specific native addon ecosystems (e.g., some ML libraries with V8-specific bindings)

When to Use Bun vs Node.js

Choose Bun when:

  • Starting a new project without legacy constraints
  • Developer experience and iteration speed matter (instant TypeScript, fast installs)
  • Building HTTP APIs where startup time and request throughput matter
  • Writing scripts, CLI tools, or automation (fast startup is critical)
  • You want fewer tools in your toolchain (no separate bundler, test runner, or package manager)

Choose Node.js when:

  • You depend on native addons that are not yet Bun-compatible
  • Your production environment requires LTS-level stability guarantees
  • You need V8-specific debugging tools (Chrome DevTools integration, --inspect)
  • Your team or organization has deep Node.js operational expertise
  • You use frameworks with known Bun incompatibilities (check framework docs)

Anti-Patterns

  • Assuming Bun is a drop-in Node.js replacement for all projects: While compatibility is high, test your specific dependencies before committing. Run your test suite under Bun early to catch issues.
  • Using bun run for everything instead of direct bun execution: For your own files, bun index.ts is sufficient. Use bun run for package.json scripts.
  • Ignoring the bun.lockb file: The binary lockfile should be committed to version control. It ensures reproducible installs across machines.
  • Mixing package managers: Do not alternate between npm, yarn, and bun install in the same project. Pick one and stick with it. Delete node_modules and the old lockfile when switching.
  • Not using Bun-native APIs: If you are running on Bun, use Bun.file() instead of fs.readFile() and Bun.serve() instead of http.createServer(). The native APIs are faster and more ergonomic.
  • Expecting identical behavior in edge cases: Bun's module resolution, error stack traces, and timing behavior can differ from Node.js in subtle ways. Write tests rather than assuming parity.

Configuration

Bun reads configuration from bunfig.toml at the project root:

# bunfig.toml

[install]
# Registry configuration
registry = "https://registry.npmjs.org/"

[install.cache]
# Global cache directory
dir = "~/.bun/install/cache"

[test]
# Test configuration
coverage = true
coverageReporter = ["text", "lcov"]

[run]
# Shell to use for bun run scripts
shell = "bash"

Environment variables can also configure Bun:

# Set memory limit
BUN_JSC_MAX_HEAP_SIZE=4096 bun run server.ts

# Enable garbage collection logging
BUN_JSC_logGC=1 bun run app.ts

Quick Reference

bun init                  # new project
bun run file.ts           # run a file
bun run script-name       # run package.json script
bun install               # install dependencies
bun add package           # add dependency
bun test                  # run tests
bun build ./src/index.ts  # bundle for production
bun --watch run file.ts   # restart on changes
bun --hot run server.ts   # hot reload on changes
bun upgrade               # upgrade bun itself
bun repl                  # interactive REPL

Install this skill directly: skilldb add bun-skills

Get CLI access →