Skip to main content
Bun includes a fast native bundler that can be used via the bun build CLI command or the Bun.build() JavaScript API. It handles TypeScript, JSX, CSS, JSON, and more out of the box without any configuration.
bun build ./index.ts --outdir ./out

Why bundle?

Fewer HTTP requests

Combine dozens of modules and dependencies into a small number of self-contained bundles that load with a single request.

Code transforms

TypeScript, JSX, CSS modules, and other formats are all converted to plain JavaScript and CSS without extra tooling.

Tree shaking

Dead code is automatically eliminated. Only the exports that are actually used end up in the bundle.

Full-stack builds

Bundle both server and client code in a single command, including HTML entry points with embedded assets.

Basic example

Given these two files:
import * as ReactDOM from "react-dom/client";
import { Component } from "./Component";

const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(<Component message="Sup!" />);
Build them with:
bun build ./index.tsx --outdir ./out
Bun writes the bundled output to ./out/index.js, with TypeScript and JSX both transpiled automatically — no tsconfig.json or Babel config required.

Targets

The --target flag controls module resolution and available globals.
bun build ./index.ts --outdir ./out --target browser
bun build ./index.ts --outdir ./out --target bun
bun build ./index.ts --outdir ./out --target node
TargetDescription
browserDefault. Prioritizes "browser" export conditions. Built-in Node.js modules are polyfilled.
bunFor bundles run by the Bun runtime. Adds a // @bun pragma to skip re-transpilation at startup.
nodePrioritizes "node" export conditions and outputs .mjs.

Output formats

Bun defaults to ESM ("esm"), with experimental support for CommonJS ("cjs").
bun build ./index.tsx --outdir ./out --format esm
bun build ./index.tsx --outdir ./out --format cjs

Key flags

FlagDescription
--outdir <dir>Directory to write output files
--outfile <file>Output file path (single entrypoint only)
--target <target>browser, bun, or node
--format <format>esm (default) or cjs
--splittingEnable code splitting for shared chunks
--minifyEnable all minification
--sourcemapSourcemap type: none, linked, external, inline
--watchRebuild on file changes
--external <pkg>Mark a package as external (not bundled)

TypeScript and JSX

TypeScript and JSX are supported natively — no plugins, no configuration.
// TypeScript types are stripped at build time
interface User {
  name: string;
  age: number;
}

// JSX is transpiled using your tsconfig.json settings
function Greeting({ name }: User) {
  return <h1>Hello, {name}</h1>;
}
Bun reads compilerOptions.jsx from your tsconfig.json to determine how to compile JSX. You can also configure it directly in the Bun.build() API:
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    runtime: "automatic",
    importSource: "preact",
  },
});
Bun does not perform type checking. Use tsc --noEmit for type checking.

Code splitting

When multiple entrypoints share dependencies, use --splitting to extract them into shared chunks, reducing duplication.
bun build ./entry-a.ts ./entry-b.ts --outdir ./out --splitting
Shared code is written to a content-hashed chunk file:
out/
├── entry-a.js
├── entry-b.js
└── chunk-2fce6291bf86559d.js

Tree shaking

Tree shaking is automatic. The bundler performs dead code elimination on all builds — unused exports and unreachable branches are removed without any configuration.
// math.ts
export function add(a: number, b: number) { return a + b; }
export function multiply(a: number, b: number) { return a * b; } // unused

// index.ts — only imports `add`
import { add } from "./math";
console.log(add(1, 2));
// `multiply` is not included in the bundle
Use /*@__PURE__*/ annotations to mark side-effect-free function calls for elimination when their result is unused:
const result = /*@__PURE__*/ expensiveSetup();

Sourcemaps

bun build ./index.ts --outdir ./out --sourcemap=linked
bun build ./index.ts --outdir ./out --sourcemap=external
bun build ./index.ts --outdir ./out --sourcemap=inline
ValueDescription
"none"No sourcemap (default)
"linked"Separate .js.map file, linked via //# sourceMappingURL comment
"external"Separate .js.map file, no comment in the bundle
"inline"Sourcemap appended inline as a base64 payload

Environment variables

Use the env option to control how process.env.* references are handled in the bundle.
# Inline all env vars
bun build ./index.tsx --outdir ./out --env inline

# Inline only vars with a specific prefix
bun build ./index.tsx --outdir ./out --env "PUBLIC_*"

JavaScript API: Bun.build()

The Bun.build() API returns a Promise<BuildOutput>:
const result = await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  target: "browser",
  format: "esm",
  splitting: true,
  minify: true,
  sourcemap: "linked",
  external: ["react", "react-dom"],
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
  },
  loader: {
    ".png": "dataurl",
    ".svg": "text",
  },
});

if (!result.success) {
  for (const log of result.logs) {
    console.error(log);
  }
}

for (const output of result.outputs) {
  console.log(output.path, output.kind);
}
Each item in result.outputs is a BuildArtifact — a Blob with extra properties:
PropertyDescription
kind"entry-point", "chunk", "asset", or "sourcemap"
pathAbsolute path to the file on disk
loaderThe loader used to process the file
hashContent hash of the file
sourcemapThe associated sourcemap artifact, if generated
If outdir is omitted, files are not written to disk. You can consume them as Blob objects or pass them directly to new Response().

Watch mode

Rebuild automatically when source files change:
bun build ./index.tsx --outdir ./out --watch

esbuild compatibility

Bun’s bundler API is modeled on esbuild. Most esbuild options map directly to Bun equivalents. Key differences:
  • Bun always bundles by default (--bundle is not needed)
  • --platform is renamed to --target
  • --outbase is renamed to --root
  • --asset-names is renamed to --asset-naming
  • No built-in dev server (use Bun.serve() instead)
  • Bun is 1.75x faster than esbuild on the three.js benchmark
Most esbuild flags map directly — see the esbuild documentation for a full flag reference.

Build docs developers (and LLMs) love