SQ3R Step 1: Survey the big picture, formulate key questions.
What is Bun?
Bun is an all-in-one toolkit for JavaScript and TypeScript applications. It ships as a single, dependency-free executable called bun. At its core is the Bun runtime — a fast JavaScript runtime designed as a drop-in replacement for Node.js. It's written in Zig and powered by Apple's JavaScriptCore engine under the hood, dramatically reducing startup times and memory usage.
Bun is more than just a runtime. It also includes:
Runtime: Execute JavaScript/TypeScript files with near-zero overhead — TS and JSX supported out of the box
Package Manager: Fast installs, workspaces, overrides, and audits with bun install
Test Runner: Jest-compatible, TypeScript-first tests with snapshots, DOM testing, and watch mode
Bundler: Native bundling for JS/TS/JSX with code splitting, plugins, and HTML imports
bun run index.tsx # TS and JSX supported out of the boxbun install <pkg> # install a packagebun build ./index.tsx # bundle a project for browsersbun test # run testsbunx cowsay 'Hello!' # execute a package
Key Questions
Before diving in, keep these questions in mind:
What fundamentally differentiates Bun from Node.js? Why switch?
Why is the Bun runtime faster than Node.js?
How does Bun's package manager compare to npm/yarn/pnpm?
How do you migrate an existing Node.js project to Bun?
Can Bun's bundler replace webpack/esbuild?
Can Bun's test runner replace Jest?
Is Bun mature enough for production use?
Technology Landscape
Bun Toolkit
├── Runtime
│ ├── JavaScriptCore engine (from Safari)
│ ├── Transpiler and runtime written in Zig
│ ├── Native TypeScript / JSX support
│ ├── ESM + CommonJS compatibility
│ └── Web-standard APIs (fetch, WebSocket, ReadableStream)
├── Package Manager
│ ├── bun install (25x faster than npm)
│ ├── Workspaces / monorepo support
│ ├── Global cache + hardlink/clonefile
│ └── Secure: lifecycle scripts disabled by default
├── Test Runner
│ ├── Jest-compatible API
│ ├── Snapshot testing / lifecycle hooks
│ ├── Watch mode / coverage reporting
│ └── DOM testing (happy-dom)
└── Bundler
├── JS/TS/JSX/CSS/HTML
├── Code splitting / Tree shaking
├── Plugin system
├── Single-file executables
└── Fullstack dev server
2. Explained Simply (Feynman Technique)
If you can't explain something in simple language, you don't truly understand it.
Core Concepts Explained
Bun is Like a Swiss Army Knife
Imagine you're a carpenter. Before, you needed a hammer (Node.js), a toolbox (npm), a testing tool (Jest), and a bundling machine (webpack). Now, Bun is like a Swiss Army knife — one tool does everything. You don't need to install and maintain four separate tools; a single bun command is enough.
Runtime = Engine + Environment APIs
JavaScript (formally, ECMAScript) is just a specification for a programming language. A JavaScript engine takes valid code and executes it (like Google's V8 or Apple's JavaScriptCore). But code needs to interact with the outside world — reading files, making network requests — which is where runtimes come in.
Browser runtimes expose APIs through the window object
Node.js provides Node-specific globals like Buffer, process, and modules like node:fs, node:http
Bun provides Node.js-compatible APIs alongside Web-standard APIs
// Bun supports both Node.js style and Web-standard APIsimport { readFileSync } from "fs"; // Node.js APIconst response = await fetch("https://api.example.com"); // Web-standard API
Package Manager = A Faster npm
# npm install: ~170ms startup timenpm install# Bun install: ~6ms startup time — 28x fasterbun install
Why so fast? Bun uses a global package cache combined with hardlinks (Linux) or clonefile (macOS), avoiding redundant downloads and file copies.
Test Runner = A Faster Jest
import { expect, test } from "bun:test";test("2 + 2", () => { expect(2 + 2).toBe(4);});
The API is nearly identical to Jest, but tests run faster because they execute directly in the Bun runtime, eliminating Node.js startup overhead.
Bundler = A Faster esbuild
bun build ./index.tsx --outdir ./out
Bun's bundler outperforms esbuild on its own three.js benchmark. It supports JS/TS/JSX/CSS/HTML out of the box.
Analogies and Metaphors
Concept
Analogy
Bun Runtime
A faster simultaneous interpreter (JavaScriptCore starts 4x faster than V8)
Bun Package Manager
A courier with a global warehouse (cache + hardlinks, no re-downloading)
Bun Test Runner
A student exam system that uses Jest syntax but runs faster
Bun Bundler
A packing machine that bundles scattered files into one package
bunfig.toml
Bun's "settings panel," like .npmrc for npm
Common Misconceptions Clarified
Myth 1: "Bun only works on macOS/Linux"
Reality: Bun fully supports Windows. On Windows, Bun uses its own shell to support bash-like syntax and common commands.
Myth 2: "Bun is incompatible with the Node.js ecosystem"
Reality: Bun aims for full compatibility with Node.js. It supports built-in modules (fs, path, http, etc.) and globals (process, Buffer). This is an ongoing effort, but most npm packages run directly in Bun.
Myth 3: "Bun isn't suitable for production"
Reality: Bun is used in production by many companies and supports deployment to AWS Lambda, Google Cloud Run, Vercel, Railway, and other major platforms.
Myth 4: "Using Bun means you can't use npm packages"
Reality: Bun's package manager is fully compatible with the npm ecosystem. If your project has a package.json, bun install works with near-zero changes.
3. Cone of Deepening (Simon's Method)
Focused effort, goal-oriented, cone-shaped deepening — start from the core, gradually expand outward.
Layer 1: Core Fundamentals
1. Installing Bun
# macOS / Linux / WSLcurl -fsSL https://bun.sh/install | bash# Windowspowershell -c "irm bun.sh/install.ps1 | iex"# Using npmnpm install -g bun# Using Homebrewbrew install oven-sh/bun/bun# Using Dockerdocker pull oven/bun
Verify installation:
bun --version
2. Runtime Basics
The Bun runtime is the core of Bun. It uses the JavaScriptCore engine (developed by Apple for Safari), which in most cases starts faster and runs faster than V8 (used by Node.js).
# Run a filebun run index.jsbun run index.ts # TypeScript runs directlybun run index.tsx # JSX runs directly# Shorthand: you can omit "run"bun index.tsx# Run a package.json scriptbun run devbun run build# Watch modebun --watch run index.tsx
Performance comparison (Linux Hello World):
Command
Startup Time
bun hello.js
5.2ms
node hello.js
25.1ms
Why is it fast? Zig language + JavaScriptCore engine + native transpiler, without Node.js's V8 startup overhead.
3. Package Manager
Bun's package manager is designed as a direct replacement for npm, yarn, and pnpm, with up to 25x speed improvement.
# Install all dependenciesbun install# Add a packagebun add reactbun add react@19.1.1 # specific versionbun add -d typescript # dev dependency# Remove a packagebun remove react# Global installbun install -g cowsay# CI/CD mode (frozen lockfile)bun ci# equivalent to bun install --frozen-lockfile
Security feature: Bun does not execute lifecycle scripts (like postinstall) of installed dependencies by default, preventing supply chain attacks. To allow scripts for a specific package, declare it in package.json:
# Run all testsbun test# Run a specific test filebun test ./test/math.test.ts# Filter tests by namebun test --test-name-pattern "addition"# Watch modebun test --watch# Concurrent executionbun test --concurrent# Coverage reportbun test --coverage
Test file patterns: *.test.{js|jsx|ts|tsx}, *_test.*, *.spec.*, *_spec.*
Lifecycle hooks:
import { beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test";beforeAll(() => { /* runs once before all tests */});beforeEach(() => { /* runs before each test */});afterEach(() => { /* runs after each test */});afterAll(() => { /* runs once after all tests */});
Mocking:
import { test, expect, mock } from "bun:test";const random = mock(() => Math.random());test("mock test", () => { const val = random(); expect(random).toHaveBeenCalled(); expect(random).toHaveBeenCalledTimes(1);});
5. Bundler
Bun's bundler is a native implementation that outperforms esbuild on its own benchmarks.
// Hot-reload routes (without server restart)server.reload({ routes: { "/api/version": () => Response.json({ version: "2.0.0" }), },});// Stop the serverawait server.stop(); // graceful stopawait server.stop(true); // force stop// Get client IPconst address = server.requestIP(req);
2. File I/O
Bun provides an optimized set of file I/O APIs.
// Read filesconst text = await Bun.file("./hello.txt").text();const buffer = await Bun.file("./data.bin").arrayBuffer();const json = await Bun.file("./config.json").json();// Check if a file existsconst exists = await Bun.file("./config.json").exists();// Get file infoconst stat = await Bun.file("./data.bin").stat();console.log(stat.size);// Write filesawait Bun.write("./output.txt", "Hello, World!");await Bun.write("./data.bin", new Uint8Array([1, 2, 3]));// Stream writingconst file = Bun.file("./large.txt");const writable = file.writer();writable.write("chunk 1\n");writable.write("chunk 2\n");writable.end();
3. SQLite (bun:sqlite)
Bun natively implements a high-performance SQLite3 driver, roughly 3-6x faster than better-sqlite3.
import { Database } from "bun:sqlite";const db = new Database(":memory:"); // or "mydb.sqlite"// Create a tabledb.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");// Insert data (prepared statement)const insert = db.prepare("INSERT INTO users (name, email) VALUES ($name, $email)");insert.run({ $name: "Alice", $email: "alice@example.com" });insert.run({ $name: "Bob", $email: "bob@example.com" });// Query dataconst user = db.query("SELECT * FROM users WHERE name = $name").get({ $name: "Alice" });// => { id: 1, name: "Alice", email: "alice@example.com" }const allUsers = db.query("SELECT * FROM users").all();// => [{ id: 1, name: "Alice", ... }, { id: 2, name: "Bob", ... }]// Transactionsconst insertMany = db.transaction((users) => { for (const user of users) { insert.run(user); }});insertMany([ { $name: "Charlie", $email: "charlie@example.com" }, { $name: "Diana", $email: "diana@example.com" },]);
Enable WAL mode for better performance:
db.run("PRAGMA journal_mode = WAL;");
Map query results to classes:
class User { name: string; email: string; get displayName() { return this.name.toUpperCase(); }}const users = db.query("SELECT name, email FROM users").as(User).all();console.log(users[0].displayName); // "ALICE"
4. WebSocket
Bun has native WebSocket support, integrated directly into Bun.serve.
const server = Bun.serve({ port: 3000, fetch(req, server) { // Upgrade HTTP request to WebSocket if (server.upgrade(req)) return; return new Response("WebSocket upgrade failed", { status: 500 }); }, websocket: { open(ws) { console.log("Client connected"); ws.subscribe("chat"); }, message(ws, message) { // Publish to all clients subscribed to "chat" server.publish("chat", message); }, close(ws) { console.log("Client disconnected"); ws.unsubscribe("chat"); }, },});
5. Environment Variables
Bun automatically loads.env files — no need to install dotenv.
// Reads directly; .env file is auto-loadedconst apiKey = process.env.API_KEY;const dbUrl = process.env.DATABASE_URL;// Load from a specific file// bun --env-file=.env.production run index.ts
6. Shell Scripting
Bun provides a built-in Shell API for running shell commands from JavaScript.
import { $ } from "bun";// Run a commandconst result = await $`echo "Hello, World!"`.text();console.log(result); // "Hello, World!\n"// Pipingconst files = await $`ls`.text();const filtered = await $`grep .ts <<< ${files}`.text();// With error handlingtry { await $`git push`;} catch (e) { console.error("Push failed:", e);}
7. Process Management (Bun.spawn)
// Spawn a child processconst proc = Bun.spawn(["echo", "Hello"], { stdout: "pipe", stderr: "pipe",});const text = await new Response(proc.stdout).text();console.log(text); // "Hello\n"const exitCode = await proc.exited;console.log(`Exit code: ${exitCode}`);
8. FFI (Foreign Function Interface)
Bun can directly call C library functions without writing native bindings.
Bun uses Apple's JavaScriptCore (JSC) engine instead of Google's V8. Key differences:
Feature
JavaScriptCore (Bun)
V8 (Node.js)
Startup speed
Faster (~5ms)
Slower (~25ms)
Memory usage
Lower
Higher
JIT compilation
More aggressive optimization
Progressive optimization
Developer
Apple (Safari)
Google (Chrome)
Why faster startup? JSC's interpreter has lower startup cost, while V8 needs more time to initialize its JIT compilation pipeline.
2. Advantages of Zig
Bun writes its runtime and transpiler in Zig. Zig's advantages:
No hidden control flow: No default exception handling, no implicit allocations — code behavior is fully predictable
Comptime execution: Code can be executed at compile time for performance optimization
C interop: Zig can directly import C header files without FFI bindings
Small binary size: Zig compiles to smaller binaries
3. Node.js Compatibility
Bun aims for full Node.js compatibility. Current status:
Supported Node.js APIs:
Built-in modules: fs, path, http, https, crypto, os, net, url, util, stream, buffer, events, child_process, and more
Globals: process, Buffer, __dirname, __filename
Node-API (N-API): supports native C++ addons
CommonJS and ES Modules
Migration guide:
# Step 1: Install Bun in an existing Node.js projectnpm install -g bun# Step 2: Install dependencies with Bun (generates bun.lock)bun install# Step 3: Run scripts with Bunbun run dev # replaces npm run devbun test # replaces npm test# Step 4: For scripts using Node.js CLI tools, use the --bun flagbun run --bun vite
Compatibility notes:
This is ongoing work — not all Node.js APIs are implemented
Most npm packages run directly
Use process.versions.bun to detect if running in Bun
4. Plugin System
Bun provides a unified plugin API that can extend both the runtime and bundler.
bun build --compile generates standalone executables, no Bun installation required
Core API Quick Reference
API / Command
Purpose
Example
bun run <file>
Run a JS/TS file
bun run index.tsx
bun install
Install dependencies
bun install / bun add react
bun test
Run tests
bun test --watch
bun build
Bundle a project
bun build ./index.tsx --outdir ./out
bunx <pkg>
Execute an npm package
bunx cowsay 'Hello'
Bun.serve()
Start an HTTP server
Bun.serve({ port: 3000, fetch })
Bun.file()
File reading
await Bun.file("./a.txt").text()
Bun.write()
File writing
await Bun.write("./out.txt", "data")
bun:sqlite
SQLite database
new Database(":memory:")
Bun.spawn()
Spawn child process
Bun.spawn(["echo", "hi"])
Bun.plugin()
Register plugin
Bun.plugin({ setup(build) {...} })
Bun.build()
Programmatic bundling
await Bun.build({ entrypoints })
$
Shell commands
await $echo hello.text()
Section Summary
Bun's core value lies in being all-in-one and fast. It integrates the four most commonly used tools in JavaScript/TypeScript development (runtime, package manager, test runner, bundler) into a single binary, achieving significant performance improvements through the JavaScriptCore engine and Zig language. For existing Node.js projects, Bun provides a highly compatible migration path with near-zero modification required.
5. Review & Practice (SQ3R · Recite & Review)
Key Takeaways
Bun is a drop-in replacement for Node.js: Uses the JavaScriptCore engine, starts 4x faster, 2.5x higher HTTP throughput
Four tools in one: Runtime, package manager, test runner, bundler — a single bun command
Package manager is 25x faster: Global cache + hardlink/clonefile, secure by default (no dependency scripts)
Test runner is Jest-compatible: Nearly identical API, low migration cost
SQLite is built-in: High-performance native driver, 3-6x faster than better-sqlite3
TypeScript with zero config: Run .ts and .tsx files directly, no ts-node needed
Web-standard APIs: fetch, WebSocket, ReadableStream, and more work out of the box