---
title: esbuild
description: Migration guide from esbuild to Bun's bundler
---
Bun's bundler API is heavily inspired by esbuild. This page is a side-by-side comparison of the two APIs.
A few behaviors differ:
**Bundling by default.** Unlike esbuild, Bun bundles by default; no `--bundle` flag is needed. To transpile each file
individually, use `Bun.Transpiler`.
**Bundler only.** Unlike esbuild, Bun's bundler has no built-in development server. Use it with `Bun.serve` and other
runtime APIs to get the same effect. esbuild's HTTP options don't apply.
## Performance
Bun's bundler is 1.75x faster than esbuild on esbuild's three.js benchmark.
Bundling 10 copies of three.js from scratch, with sourcemaps and minification
## CLI API
```bash terminal icon="terminal"
# esbuild
esbuild --outdir=out --bundle
# bun
bun build --outdir=out
```
In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take one, like `--outdir `, can be written as `--outdir out` or `--outdir=out`. Some flags, like `--define`, can be repeated: `--define foo=bar --define bar=baz`.
| esbuild | bun build | Notes |
| ---------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--bundle` | n/a | Bun always bundles; use `--no-bundle` to disable it. |
| `--define:K=V` | `--define K=V` | Small syntax difference; no colon.
`esbuild --define:foo=bar`
`bun build --define foo=bar` |
| `--external:` | `--external ` | Small syntax difference; no colon.
`esbuild --external:react`
`bun build --external react` |
| `--format` | `--format` | Bun supports `"esm"`, `"cjs"`, and `"iife"`. esbuild defaults to `"iife"`. |
| `--loader:.ext=loader` | `--loader .ext:loader` | Bun supports a different set of built-in loaders than esbuild; see [loaders](/bundler/loaders). The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not implemented.
The syntax for `--loader` differs.
`esbuild app.ts --bundle --loader:.svg=text`
`bun build app.ts --loader .svg:text` |
| `--minify` | `--minify` | No differences |
| `--outdir` | `--outdir` | No differences |
| `--outfile` | `--outfile` | No differences |
| `--packages` | `--packages` | No differences |
| `--platform` | `--target` | Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. |
| `--serve` | n/a | Not applicable |
| `--sourcemap` | `--sourcemap` | Supports `linked` (the default when no value is given), `external`, `inline`, and `none`. Does not support esbuild's `both`. |
| `--splitting` | `--splitting` | No differences |
| `--target` | n/a | Not supported. Bun's bundler performs no syntactic down-leveling. |
| `--watch` | `--watch` | No differences |
| `--allow-overwrite` | n/a | Bun never allows overwriting |
| `--analyze` | n/a | Not supported |
| `--asset-names` | `--asset-naming` | Renamed for consistency with naming in JS API |
| `--banner` | `--banner` | Only applies to js bundles |
| `--footer` | `--footer` | Only applies to js bundles |
| `--certfile` | n/a | Not applicable |
| `--charset=utf8` | n/a | Not supported |
| `--chunk-names` | `--chunk-naming` | Renamed for consistency with naming in JS API |
| `--color` | n/a | Always enabled |
| `--drop` | `--drop` | |
| n/a | `--feature` | Bun-specific. Enables feature flags for compile-time dead-code elimination through `import { feature } from "bun:bundle"` |
| `--entry-names` | `--entry-naming` | Renamed for consistency with naming in JS API |
| `--global-name` | n/a | Not supported |
| `--ignore-annotations` | `--ignore-dce-annotations` | |
| `--inject` | n/a | Not supported |
| `--jsx` | `--jsx-runtime ` | Supports `"automatic"` (uses jsx transform) and `"classic"` (uses `React.createElement`) |
| `--jsx-dev` | n/a | Bun reads `compilerOptions.jsx` from `tsconfig.json` to determine a default. If `compilerOptions.jsx` is `"react-jsx"`, or if `NODE_ENV=production`, Bun uses the jsx transform. Otherwise, it uses `jsxDEV`. The bundler does not support `preserve`. |
| `--jsx-factory` | `--jsx-factory` | |
| `--jsx-fragment` | `--jsx-fragment` | |
| `--jsx-import-source` | `--jsx-import-source` | |
| `--jsx-side-effects` | `--jsx-side-effects` | |
| `--keep-names` | `--keep-names` | |
| `--keyfile` | n/a | Not applicable |
| `--legal-comments` | n/a | Not supported |
| `--log-level` | n/a | Not supported. You can set the log level in `bunfig.toml` as `logLevel`. |
| `--log-limit` | n/a | Not supported |
| `--log-override:X=Y` | n/a | Not supported |
| `--main-fields` | n/a | Not supported |
| `--mangle-cache` | n/a | Not supported |
| `--mangle-props` | n/a | Not supported |
| `--mangle-quoted` | n/a | Not supported |
| `--metafile` | `--metafile` | |
| `--minify-whitespace` | `--minify-whitespace` | |
| `--minify-identifiers` | `--minify-identifiers` | |
| `--minify-syntax` | `--minify-syntax` | |
| `--out-extension` | n/a | Not supported |
| `--outbase` | `--root` | |
| `--preserve-symlinks` | n/a | Not supported |
| `--public-path` | `--public-path` | |
| `--pure` | n/a | Not supported |
| `--reserve-props` | n/a | Not supported |
| `--resolve-extensions` | n/a | Not supported |
| `--servedir` | n/a | Not applicable |
| `--source-root` | n/a | Not supported |
| `--sourcefile` | n/a | Not supported. Bun does not support stdin input. |
| `--sources-content` | n/a | Not supported |
| `--supported` | n/a | Not supported |
| `--tree-shaking` | n/a | Always true |
| `--tsconfig` | `--tsconfig-override` | |
| `--version` | n/a | Run `bun --version` to see the version of Bun. |
## JavaScript API
| esbuild.build() | Bun.build() | Notes |
| ------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `absWorkingDir` | n/a | Always set to `process.cwd()` |
| `alias` | n/a | Not supported |
| `allowOverwrite` | n/a | Always false |
| `assetNames` | `naming.asset` | Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` |
| `banner` | `banner` | Only applies to js bundles |
| `bundle` | n/a | Always true. Use `Bun.Transpiler` to transpile without bundling. |
| `charset` | n/a | Not supported |
| `chunkNames` | `naming.chunk` | Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` |
| `color` | n/a | Bun returns logs in the `logs` property of the build result. |
| `conditions` | `conditions` | No differences |
| `define` | `define` | |
| `drop` | `drop` | |
| `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses the same templating syntax as esbuild, but you must include `[ext]` explicitly.
`ts
Bun.build({
entrypoints: ["./index.tsx"],
// when string, this is equivalent to entryNames
naming: "[name].[ext]",
// granular naming options
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` |
| `entryPoints` | `entrypoints` | Capitalization difference |
| `external` | `external` | No differences |
| `footer` | `footer` | Only applies to js bundles |
| `format` | `format` | Supports `"esm"`, `"cjs"`, and `"iife"`. |
| `globalName` | n/a | Not supported |
| `ignoreAnnotations` | `ignoreDCEAnnotations` | |
| `inject` | n/a | Not supported |
| `jsx` | `jsx.runtime` | Supports `"automatic"` and `"classic"` |
| `jsxDev` | `jsx.development` | |
| `jsxFactory` | `jsx.factory` | |
| `jsxFragment` | `jsx.fragment` | |
| `jsxImportSource` | `jsx.importSource` | |
| `jsxSideEffects` | `jsx.sideEffects` | |
| `keepNames` | `minify.keepNames` | |
| `legalComments` | n/a | Not supported |
| `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see [loaders](/bundler/loaders). Bun does not implement the esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty`. |
| `logLevel` | n/a | Not supported |
| `logLimit` | n/a | Not supported |
| `logOverride` | n/a | Not supported |
| `mainFields` | n/a | Not supported |
| `mangleCache` | n/a | Not supported |
| `mangleProps` | n/a | Not supported |
| `mangleQuoted` | n/a | Not supported |
| `metafile` | `metafile` | |
| `minify` | `minify` | In Bun, `minify` can be a boolean or an object.
`ts
await Bun.build({
entrypoints: ['./index.tsx'],
// enable all minification
minify: true
// granular options
minify: {
identifiers: true,
syntax: true,
whitespace: true
}
})
` |
| `minifyIdentifiers` | `minify.identifiers` | See `minify` |
| `minifySyntax` | `minify.syntax` | See `minify` |
| `minifyWhitespace` | `minify.whitespace` | See `minify` |
| `nodePaths` | n/a | Not supported |
| `outExtension` | n/a | Not supported |
| `outbase` | `root` | Different name |
| `outdir` | `outdir` | No differences |
| `outfile` | `outfile` | No differences |
| `packages` | `packages` | No differences |
| `platform` | `target` | Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. |
| `plugins` | `plugins` | Bun's plugin API is a subset of esbuild's. Some esbuild plugins work with Bun without modification. |
| `preserveSymlinks` | n/a | Not supported |
| `publicPath` | `publicPath` | No differences |
| `pure` | n/a | Not supported |
| `reserveProps` | n/a | Not supported |
| `resolveExtensions` | n/a | Not supported |
| `sourceRoot` | n/a | Not supported |
| `sourcemap` | `sourcemap` | Supports `"none"`, `"linked"`, `"inline"`, and `"external"` |
| `sourcesContent` | n/a | Not supported |
| `splitting` | `splitting` | No differences |
| `stdin` | n/a | Not supported |
| `supported` | n/a | Not supported |
| `target` | n/a | No support for syntax downleveling |
| `treeShaking` | `treeShaking` | Defaults to `true` |
| `tsconfig` | `tsconfig` | |
| `write` | n/a | Set to true if `outdir`/`outfile` is set, otherwise false |
## Plugin API
Bun's plugin API is designed to be esbuild-compatible. Bun doesn't support esbuild's entire plugin API surface, but it implements the core functionality. Many third-party esbuild plugins work with Bun without modification.
Long term, we aim for feature parity with esbuild's API. If something doesn't work, file an issue to help us
prioritize.
In both Bun and esbuild, you define plugins with a builder object.
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(builder) {
// define plugin
},
};
```
The builder object's methods hook into parts of the bundling process. Bun implements `onStart`, `onEnd`, `onResolve`, and `onLoad`; it does not implement the esbuild hooks `onDispose` and `resolve`. Bun partially implements `initialOptions`: the object is read-only and exposes only a subset of esbuild's options. Use `config` (the same thing in Bun's `BuildConfig` format) instead.
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(builder) {
builder.onStart(() => {
/* called when the bundle starts */
});
builder.onResolve(
{
/* onResolve.options */
},
args => {
return {
/* onResolve.results */
};
},
);
builder.onLoad(
{
/* onLoad.options */
},
args => {
return {
/* onLoad.results */
};
},
);
builder.onEnd(result => {
/* called when the bundle is complete */
});
},
};
```
### onResolve
- 🟢 `filter`
- 🟢 `namespace`
- 🟢 `path`
- 🟢 `importer`
- 🟢 `namespace`
- 🟢 `resolveDir`
- 🟢 `kind`
- 🔴 `pluginData`
- 🟢 `namespace`
- 🟢 `path`
- 🔴 `errors`
- 🟢 `external`
- 🔴 `pluginData`
- 🔴 `pluginName`
- 🔴 `sideEffects`
- 🔴 `suffix`
- 🔴 `warnings`
- 🔴 `watchDirs`
- 🔴 `watchFiles`
### onLoad
- 🟢 `filter`
- 🟢 `namespace`
- 🟢 `path`
- 🟢 `namespace`
- 🔴 `suffix`
- 🔴 `pluginData`
- 🟢 `contents`
- 🟢 `loader`
- 🔴 `errors`
- 🔴 `pluginData`
- 🔴 `pluginName`
- 🔴 `resolveDir`
- 🔴 `warnings`
- 🔴 `watchDirs`
- 🔴 `watchFiles`