JavaScript Runtime · Reference Cheat Sheet

bun

v1.4 JavaScriptCore · Rust core

One dependency-free binary that is a runtime, package manager, test runner and bundler at once — running TypeScript & JSX natively with no build step. A drop-in for Node.js, npm, jest and webpack.

run & scripts package manager test runner bundler / compile Bun-native APIs setup & config compat / gotcha ★ most common
Verified by running Bun 1.3.14 in-container (init · add · run · test · build · --compile · Bun API introspection) & cross-checked against bun.com/docs. Re-verified 2026-08-28; current release Bun 1.4 (core rewritten Zig→Rust: ~20% smaller binary, ~50% faster start, +1,517 Node-compat tests). See footer for sources.

The mental model — one binary replaces the whole toolchain

A · ONE BINARY, FOUR TOOLS bun single · ~90MB · zero deps Runtime bun index.ts · native TS/JSX → replaces node Package manager bun install · 10–25× faster → replaces npm / yarn / pnpm Test runner bun test · Jest-compatible → replaces jest / vitest Bundler + compile bun build · single-file exe → replaces webpack / esbuild … plus batteries included (no extra installs) Bun.serve()HTTP + WebSocket server Bun.file / writefast file I/O (Blob-based) Bun.$`…`cross-platform shell bun:sqlitebuilt-in SQLite driver Bun.passwordArgon2 hashing .env auto-loadno dotenv · ts-node · tsc strips native TS/JSX in-memory · Web-standard fetch / Response / WebSocket / ReadableStream B · THE EVERYDAY LOOP bun init bun install bun run index.ts bun test bun build --compile ./myapp (exe) creates package.json + tsconfig · then the three project files stay in lockstep: package.jsonbun.lock (text) → node_modules/ real output captured this build · bun 1.3.14 $ bun add zod Resolved, downloaded and extracted [4] · Saved lockfile installed zod@4.4.3  —  1 package installed [193.00ms] $ bun test (pass) math > adds · (pass) math > async 2 pass · 0 fail — Ran 2 tests across 1 file [20ms]

Key insight: bun <file> runs TS/JSX directly (types stripped in-memory — no .js emitted, no ts-node); bun run <name> runs a package.json script; bunx <pkg> runs a package binary, installing on demand.

Part I

Runtime & CLI

01Install & upgrade
  • curl -fsSL https://bun.com/install | bash # macOS / Linux / WSL
    Official installer — one dependency-free binary.
  • npm install -g bun # or: brew install oven-sh/bun/bun
    Also on npm, Homebrew, Scoop, Docker (oven/bun).
  • bun upgrade # self-update to latest
  • bun --version # 1.4.x bun --revision # +git sha
  • powershell -c "irm bun.com/install.ps1 | iex" # Windows
02Run files & scripts
⇆ replaces node / ts-node / nodemon
  • bun index.ts # run TS/JS/JSX directly, no build step
    Native TS/JSX — no tsc, no config.
  • bun run start # run a package.json script
    Bare bun run lists all scripts.
  • bun run --watch dev # restart on file change
  • bun --hot server.ts # hot reload, keeps state / server up
    Great for servers.
  • bun -e 'console.log(1+2)' # eval bun -p expr # eval + print
  • bun repl # interactive REPL
  • bun --filter '*' build # run script across all workspaces
03Everyday flags
  • --watch / --hot
    Restart on change vs live hot-reload (state kept).
  • -b, --bun
    Force a script/binary to use Bun’s runtime, not Node.
  • --smol
    Low-memory mode (GC more often).
  • -r, --preload=./setup.ts
    Run a module before your entrypoint.
  • --env-file=.env.prod
    Load specific env file(s).
  • --inspect / --inspect-brk
    Attach Bun’s debugger.
  • --port=4000
    Default port for Bun.serve.
  • --if-present
    Exit 0 if the script/entry is missing.
04TypeScript & JSX
  • Runs .ts .tsx .jsx .mjs .cjs with zero config.
    Types are stripped in-memory; nothing is emitted.
  • Bun does not type-check.
    Use tsc --noEmit (or bunx tsc) in CI for that.
  • Reads tsconfig.json for paths & JSX options.
  • bunfig.toml → [run], [test], [install] settings.
  • Text imports: import s from "./x.txt", with { type: "json" }.
05Environment & .env
  • .env, .env.local, .env.<NODE_ENV> auto-loaded.
    No dotenv needed.
  • Bun.env.KEY  or  process.env.KEY
  • bun --env-file=.env.ci run x
    Override which file loads.
  • NODE_ENV=production bun run start
  • Inline into a bundle: bun build --env=PUBLIC_*
Part II

Package Manager

06Install dependencies
⇆ replaces npm / yarn / pnpm
  • bun install # alias: bun i — reads package.json
    10–25× faster than npm; global hard-linked cache.
  • bun install --production # skip devDependencies
    True
  • bun install --frozen-lockfile # CI: fail if lock would change
    Deterministic installs.
  • bun install --linker isolated # pnpm-style, non-hoisted
  • bun install --filter './packages/*' # workspace subset
  • bun install -g typescript # global install
07Add · remove · update
  • bun add zod # add + install (writes package.json)
  • bun add -d typescript # devDependency (--dev)
    True
  • bun add --peer react bun add --optional fsevents
  • bun add -E zod@4.4.3 # exact pin, no ^range
  • bun add react@latest bun add pkg@next # tags
  • bun remove webpack # alias: bun rm
  • bun update bun update --latest # bump ranges
    Bun 1.4: in-place transitive updates are now the default; nested version-scoped overrides supported.
08Lockfile & reproducibility
  • bun.locktext lockfile, the default.
    Commit it. (Legacy binary bun.lockb is gone by default.)
  • Git-ignore node_modules/; commit package.json + bun.lock.
    True
  • bun install --frozen-lockfile # CI reproducible
  • bun pm migrate # import package-lock.json / yarn.lock
  • bun install --save-text-lockfile # force text form
09Workspaces / monorepo
  • Root package.json: "workspaces": ["packages/*"]
  • bun install # at root — links every workspace pkg
  • bun run --filter './apps/*' build # -F pattern
    Run a script across matching packages.
  • bun add lodash --filter web # add to one workspace
  • Catalogs & "overrides" pin versions repo-wide.
10Run binaries (bunx)
⇆ replaces npx
  • bunx cowsay hi # = bun x — run a pkg CLI, install if missing
    Cached & fast; no global clutter.
  • bunx --bun vite # force the tool onto Bun's runtime
  • bunx -p create-react-app my-app # bin name != pkg name
  • bun create next-app ./app # scaffold from a template
  • bun init -y # empty project (package.json, tsconfig, index.ts)
11Inspect & maintain
  • bun outdated # show upgradable deps
  • bun why zod # explain why a pkg is installed
  • bun info react # registry metadata (bun pm view)
  • bun audit bun audit --fix # scan + auto-fix (1.4)
  • bun pm ls --all bun pm cache rm bun pm pack
  • bun link bun link my-pkg # local dev linking
  • bun patch react # edit an installed dep, save a patch
  • bun publish # publish to npm registry
Part III

Bundler & Test Runner

12Bundle
⇆ replaces webpack / esbuild
  • bun build ./index.ts --outdir dist # bundle TS/JS/JSX/CSS
    esbuild-class speed, tree-shaking, splitting.
  • bun build src/app.ts --target browser # bun | node | browser
    True
  • bun build ... --minify --sourcemap linked
  • bun build ... --splitting --format esm
    Code-split shared chunks.
  • bun build ... --external react # keep dep unbundled
  • JS API: await Bun.build({ entrypoints, outdir, minify })
13Compile & plugins
  • bun build ./cli.ts --compile --outfile mycli
    Standalone executable — ships Bun + your code.
  • bun build ... --compile --target=bun-windows-x64
    Cross-compile.
  • bun build ... --compile --bytecode # faster startup
  • Plugins: Bun.plugin({ setup(b){ b.onLoad(...) } })
    Same API for bundler & runtime loaders.
  • Loaders for .svg .txt .wasm, and macros (with { type: "macro" }).
14Test
⇆ replaces jest / vitest
  • bun test # runs *.test.ts, Jest-compatible
    Built-in — no install, TS-first.
  • import { test, expect, describe, it } from "bun:test"
    Import from bun:test.
  • bun test --watch bun test -t "pattern"
    True
  • bun test --coverage # text / lcov reporters
  • test.only · test.skip · test.todo · test.each
  • bun test --bail=1 --randomize --isolate
15Matchers & mocks
  • expect(x).toBe / .toEqual / .toStrictEqual
  • .toThrow() · .toContain() · .toMatchSnapshot()
    Snapshots built in.
  • .resolves / .rejects for async.
  • import { mock, spyOn } from "bun:test"
  • const fn = mock(() => 1); expect(fn).toHaveBeenCalled()
  • beforeEach · afterEach · beforeAll · afterAll
  • DOM tests via happy-dom; --update-snapshots to refresh.
Part IV

Bun-native APIs

16HTTP server & WebSocket
  • Bun.serve({ port: 3000, fetch(req){ return new Response("hi") } })
    Standard RequestResponse.
  • routes: { "/api/:id": req => Response.json({...}) }
    Built-in router.
  • websocket: { message(ws, m){ ws.send(m) } } + server.upgrade(req)
    WebSockets in the same server.
  • Bun.serve({ tls: { cert, key } })
    HTTPS built in.
  • Return a Bun.file() to stream static files.
17File I/O & shell
  • const f = Bun.file("a.json"); await f.json() / .text() / .bytes()
    Lazy Blob reference.
  • await Bun.write("out.txt", "data")
    Strings, Blobs, Responses, files.
  • import { $ } from "bun"; await $`ls -la | grep ts`.text()
    Cross-platform shell, no deps.
  • Bun.spawn(["ffmpeg", ...]) / Bun.spawnSync
    Child processes.
  • new Bun.Glob("**/*.ts").scan(".")
    Fast globbing.
  • Bun.stdin · Bun.stdout · Bun.stderr
18Data & crypto
  • import { Database } from "bun:sqlite"
    Built-in SQLite — no better-sqlite3.
  • db.query("SELECT * FROM t WHERE x=?").all(1)
  • import { sql } from "bun"; await sql`select 1` (Postgres)
  • Bun.redis (Valkey/Redis) · new Bun.S3Client(...)
  • await Bun.password.hash(pw) / .verify(pw, hash)
    Argon2 by default.
  • Bun.hash(data) · new Bun.CryptoHasher("sha256")
19Utilities
  • Bun.sleep(200) · Bun.sleepSync(ms) · Bun.nanoseconds()
  • Bun.which("node") · Bun.env · Bun.main · Bun.version
  • Bun.randomUUIDv7() · Bun.randomUUIDv5()
  • Bun.color("#f06", "css") · Bun.semver.satisfies(...)
  • Bun.gzipSync / gunzipSync · zstdCompressSync
  • Bun.TOML.parse · Bun.YAML.parse · Bun.inspect(obj)
  • Bun.escapeHTML · Bun.stringWidth · Bun.deepEquals
20Node compat & migration
  • Drop-in for Node: process, Buffer, fs, path, http work.
    ~99% API compatibility — but not 100%.
  • Some native C++ addons & a few fs/edge APIs still differ.
    Check the Node-compat page before migrating prod.
  • bun --bun run next dev # force Node tools onto Bun
  • bun pm migrate # convert existing npm/yarn/pnpm lockfile
  • Uses JavaScriptCore (Safari), not V8 — fast startup.

Four ideas worth a diagram

The distinctions that trip people up most.

1 · One binary, not eight installs

Everything a Node project usually pulls in, collapsed into bun.

NODE STACK — many tools node npm jest webpack tsc ts-node nodemon dotenv + esbuild, prettier configs, … bun bun install once, run everything

2 · bun run vs bunx vs bun <file>

Three execution modes — the #1 source of confusion.

bun app.ts bun run dev bunx cowsay a file on disk → execute directly a script name in package.json a package binary → install if missing JavaScriptCore transpiles TS/JSX in-memory, then runs it. No build step, no config. pulls binary into a shared cache, runs it. Gotcha: if a script and a file share a name, bun run x prefers the script. bunx == bun x · like npx but cached and faster.

3 · Three project files in lockstep

You declare loose ranges; Bun resolves an exact, hashed, text lockfile.

package.json you declare "zod": "^4" "react": "^19" loose ranges COMMIT ✓ resolve bun.lock exact + hashed (text) zod@4.4.3 react@19.1.0 sha512-… reproducible COMMIT ✓ install node_modules/ the installed env hard-linked from cache .gitignore ✗

4 · How Bun runs your .ts

No tsc, no emitted .js — and no type-checking either.

app.ts types + JSX Bun transpiler strips types, JSX→JS in memory JavaScriptCore executes · fast start No emitted files · no ts-node needed Types are erased, not checked. Bun will happily run code with type errors. For real type-safety run tsc --noEmit (or bunx tsc) in CI.

Worth memorizing

bun <file>runs TS/JSX directly — no build, no config, no .js emitted.
bun run <name>runs a package.json script; bare bun run lists them.
bunx = bun xrun a package binary, auto-installing to a shared cache (like npx, faster).
bun installnpm-compatible; writes bun.lock (text). Commit it; git-ignore node_modules.
--frozen-lockfileuse in CI for deterministic, no-re-resolve installs.
bun add -d / -E-d dev · --peer · --optional · -E exact pin.
--watch vs --hot--watch restarts the process; --hot live-reloads and keeps state.
bun:testtests import from bun:test — Jest-compatible, not jest itself.
bun build --compileemits a standalone executable (Bun runtime + your code).
.env auto-loadsno dotenv; use Bun.env / process.env; --env-file to override.
bun --bun run xforce a Node tool (vite, next…) onto Bun’s runtime via a node symlink.
batteries includedBun.serve, Bun.file, Bun.$, bun:sqlite, Bun.password — no installs.
no type-checkingBun erases types; run tsc --noEmit separately for safety.
JavaScriptCoreengine is Safari’s JSC, not V8 — ~99% Node API compat, not 100%.
bun pm migrateimport an existing package-lock.json / yarn.lock / pnpm-lock.
bun upgradeself-update the binary; pin Bun in CI (pre-2.0, minors can shift).