Bun Build
Bun is an all-in-one JavaScript toolkit (runtime, package manager, test runner, bundler). bun build is its native bundler — fast, TypeScript-aware, and useful for browser bundles, server-side bundles, and executables. It competes most directly with esbuild on speed and API simplicity.
Adopt Bun build when your team already runs Bun, or when you want a single binary for transpile+bundle without wiring esbuild. Prefer Vite/Rspack when you need a mature plugin ecosystem, complex CSS pipelines, or Module Federation.
info
| 1 | # macOS / Linux |
| 2 | curl -fsSL https://bun.sh/install | bash |
| 3 | |
| 4 | # Verify |
| 5 | bun --version |
| 6 | |
| 7 | # Project-local scripts still call `bun build` once Bun is on PATH |
| 1 | # Browser-oriented bundle |
| 2 | bun build ./src/index.ts --outdir=dist --target=browser |
| 3 | |
| 4 | # Server / Node-compatible |
| 5 | bun build ./src/server.ts --outdir=dist --target=node |
| 6 | |
| 7 | # Bun runtime target |
| 8 | bun build ./src/cli.ts --outdir=dist --target=bun |
| 9 | |
| 10 | # Minify + sourcemap |
| 11 | bun build ./src/index.ts --outdir=dist --minify --sourcemap |
| 12 | |
| 13 | # Code splitting |
| 14 | bun build ./src/index.ts --outdir=dist --splitting |
| 15 | |
| 16 | # Watch |
| 17 | bun build ./src/index.ts --outdir=dist --watch |
| 18 | |
| 19 | # Compile to a standalone executable (Bun feature) |
| 20 | bun build ./src/cli.ts --compile --outfile=mycli |
| Flag / option | Purpose | When |
|---|---|---|
| --target | browser / node / bun | Always set explicitly |
| --minify | Shrink production output | Prod browser bundles |
| --sourcemap | Debug mapping | Almost always in prod (hidden/upload) |
| --splitting | Shared chunk extraction | Multi-entry or dynamic import apps |
| --compile | Single binary CLI | Distributing tools without Node install |
| 1 | const result = await Bun.build({ |
| 2 | entrypoints: ["./src/index.ts"], |
| 3 | outdir: "./dist", |
| 4 | target: "browser", |
| 5 | minify: true, |
| 6 | sourcemap: "external", |
| 7 | splitting: true, |
| 8 | naming: "[dir]/[name]-[hash].[ext]", |
| 9 | define: { |
| 10 | "process.env.NODE_ENV": JSON.stringify("production"), |
| 11 | }, |
| 12 | external: ["react", "react-dom"], // for library-like outputs |
| 13 | }); |
| 14 | |
| 15 | if (!result.success) { |
| 16 | console.error(result.logs); |
| 17 | process.exit(1); |
| 18 | } |
| 19 | |
| 20 | for (const output of result.outputs) { |
| 21 | console.log(output.path, output.size); |
| 22 | } |
note
Target selection changes how Bun resolves builtins, polyfills, and module formats.
| Target | Use for | Watch outs |
|---|---|---|
| browser | SPA bundles, widgets | Do not pull Node builtins |
| node | Services running on Node | Externalize native addons |
| bun | Apps that run under Bun | May use Bun-only APIs |
| 1 | { |
| 2 | "scripts": { |
| 3 | "build:web": "bun build ./src/client.ts --outdir=dist/client --target=browser --minify --sourcemap", |
| 4 | "build:api": "bun build ./src/server.ts --outdir=dist/server --target=node --sourcemap", |
| 5 | "build": "bun run build:web && bun run build:api" |
| 6 | } |
| 7 | } |
--minify reduces identifiers and whitespace. For libraries, prefer unminified dual packages so consumers control minification. For end-user browser apps, minify production builds.
best practice
| Aspect | Bun build | esbuild |
|---|---|---|
| Speed | Very fast (native) | Very fast (Go) |
| Ecosystem | Growing with Bun | Widely embedded (Vite, tsup) |
| Plugin model | Bun plugins | esbuild plugins |
| Node portability | Needs Bun to run builder | Runs anywhere Node runs |
| Extras | --compile, tight runtime integration | Mature transform API, metafile |
| Best fit | Bun-centric stacks | Tooling inside Vite/tsup/CI Node images |
pro tip
Bun can bundle from HTML entry points and handle many asset types. For complex Tailwind/PostCSS/app shells, Vite still provides a richer DX (plugin ecosystem, framework integrations).
| 1 | # HTML as entry (feature set evolves — check current Bun docs) |
| 2 | bun build ./index.html --outdir=dist --target=browser --minify |
- Install Bun on runners (oven-sh/setup-bun on GitHub Actions).
- Cache Bun's install cache / lockfile (bun.lock).
- Pin Bun version — bundler behavior changes across releases.
- Do not assume Node can execute Bun.build scripts.
| 1 | jobs: |
| 2 | build: |
| 3 | runs-on: ubuntu-latest |
| 4 | steps: |
| 5 | - uses: actions/checkout@v4 |
| 6 | - uses: oven-sh/setup-bun@v2 |
| 7 | with: |
| 8 | bun-version: "1.2.17" |
| 9 | - run: bun install --frozen-lockfile |
| 10 | - run: bun run build |
Bun supports a plugin API for custom loaders (similar in spirit to esbuild plugins). Use plugins sparingly — prefer built-in TypeScript/JSX/JSON support first.
| 1 | await Bun.build({ |
| 2 | entrypoints: ["./src/index.ts"], |
| 3 | outdir: "./dist", |
| 4 | target: "browser", |
| 5 | plugins: [ |
| 6 | { |
| 7 | name: "raw-text", |
| 8 | setup(build) { |
| 9 | build.onLoad({ filter: /\.txt$/ }, async (args) => { |
| 10 | const text = await Bun.file(args.path).text(); |
| 11 | return { |
| 12 | contents: `export default ${JSON.stringify(text)}`, |
| 13 | loader: "js", |
| 14 | }; |
| 15 | }); |
| 16 | }, |
| 17 | }, |
| 18 | ], |
| 19 | }); |
For npm libraries, tsup remains the safer default on Node CI. If you still use Bun, externalize peers and emit formats your exports map expects.
| 1 | await Bun.build({ |
| 2 | entrypoints: ["./src/index.ts"], |
| 3 | outdir: "./dist", |
| 4 | target: "node", |
| 5 | format: "esm", |
| 6 | external: ["react", "react-dom"], |
| 7 | sourcemap: "external", |
| 8 | }); |
note
| Use Bun build when | Prefer alternatives when |
|---|---|
| Team already on Bun runtime | Need Webpack loaders / federation |
| Shipping a compiled CLI | Need Vite framework templates |
| Simple fast bundles | Need Node-only CI without Bun |
| Prototyping without Vite | Complex CSS / SSR frameworks |
warning
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.