> ## Documentation Index
> Fetch the complete documentation index at: https://bun-1dd33a4e-farm-084c10c9-docs-lifecycle-supported-scripts.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# esbuild

> 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:

<Note>
  **Bundling by default.** Unlike esbuild, Bun bundles by default; no `--bundle` flag is needed. To transpile each file
  individually, use `Bun.Transpiler`.
</Note>

<Note>
  **Bundler only.** Unlike esbuild, Bun's bundler has no built-in development server or file watcher. Use it with
  `Bun.serve` and other runtime APIs to get the same effect. esbuild's HTTP and file-watching options don't apply.
</Note>

## Performance

Bun's bundler is 1.75x faster than esbuild on esbuild's three.js benchmark.

<Info>Bundling 10 copies of three.js from scratch, with sourcemaps and minification</Info>

## CLI API

```bash terminal icon="terminal" theme={null}
# esbuild
esbuild <entrypoint> --outdir=out --bundle

# bun
bun build <entrypoint> --outdir=out
```

In Bun's CLI, boolean flags like `--minify` take no argument. Flags that take one, like `--outdir <path>`, 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.<br />`esbuild --define:foo=bar`<br />`bun build --define foo=bar` |
| `--external:<pkg>` | `--external <pkg>` | Small syntax difference; no colon.<br />`esbuild --external:react`<br />`bun build --external react` |
| `--format` | `--format` | Bun supports `"esm"` and `"cjs"`; more module formats are planned. 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.<br /><br />The syntax for `--loader` differs.<br />`esbuild app.ts --bundle --loader:.svg=text`<br />`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` | No differences |
| `--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 | Overwriting is never allowed |
| `--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 applicable; Bun does not support `iife` output |
| `--ignore-annotations` | `--ignore-dce-annotations` | |
| `--inject` | n/a | Not supported |
| `--jsx` | `--jsx-runtime <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` | n/a | JSX is always assumed to be side-effect-free |
| `--keep-names` | n/a | Not supported |
| `--keyfile` | n/a | Not applicable |
| `--legal-comments` | n/a | Not supported |
| `--log-level` | n/a | Not supported. This can be set 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` | n/a | Not supported |
| `--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. |
| `--sourcemap` | `--sourcemap` | No differences |
| `--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 `[ext]` must be included explicitly.<br /><br />`ts<br/>Bun.build({<br/>  entrypoints: ["./index.tsx"],<br/>  naming: {<br/>    asset: "[name].[ext]",<br/>  },<br/>});<br/>` |
| `banner` | n/a | Not supported |
| `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 `[ext]` must be included explicitly.<br /><br />`ts<br/>Bun.build({<br/>  entrypoints: ["./index.tsx"],<br/>  naming: {<br/>    chunk: "[name].[ext]",<br/>  },<br/>});<br/>` |
| `color` | n/a | Bun returns logs in the `logs` property of the build result. |
| `conditions` | n/a | Not supported. Export conditions priority is determined by `target`. |
| `define` | `define` | |
| `drop` | n/a | Not supported |
| `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 `[ext]` must be included explicitly.<br /><br />`ts<br/>Bun.build({<br/>  entrypoints: ["./index.tsx"],<br/>  // when string, this is equivalent to entryNames<br/>  naming: "[name].[ext]",<br/><br/>  // granular naming options<br/>  naming: {<br/>    entry: "[name].[ext]",<br/>    asset: "[name].[ext]",<br/>    chunk: "[name].[ext]",<br/>  },<br/>});<br/>` |
| `entryPoints` | `entrypoints` | Capitalization difference |
| `external` | `external` | No differences |
| `footer` | n/a | Not supported |
| `format` | `format` | Only supports `"esm"`. Support for `"cjs"` and `"iife"` is planned. |
| `globalName` | n/a | Not supported |
| `ignoreAnnotations` | n/a | Not supported |
| `inject` | n/a | Not supported |
| `jsx` | `jsx` | Not supported in the JS API; configure in `tsconfig.json` |
| `jsxDev` | `jsxDev` | Not supported in the JS API; configure in `tsconfig.json` |
| `jsxFactory` | `jsxFactory` | Not supported in the JS API; configure in `tsconfig.json` |
| `jsxFragment` | `jsxFragment` | Not supported in the JS API; configure in `tsconfig.json` |
| `jsxImportSource` | `jsxImportSource` | Not supported in the JS API; configure in `tsconfig.json` |
| `jsxSideEffects` | `jsxSideEffects` | Not supported in the JS API; configure in `tsconfig.json` |
| `keepNames` | n/a | Not supported |
| `legalComments` | n/a | Not supported |
| `loader` | `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. |
| `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` | n/a | Not supported |
| `minify` | `minify` | In Bun, `minify` can be a boolean or an object.<br /><br />`ts<br/>await Bun.build({<br/>  entrypoints: ['./index.tsx'],<br/>  // enable all minification<br/>  minify: true<br/><br/>  // granular options<br/>  minify: {<br/>    identifiers: true,<br/>    syntax: true,<br/>    whitespace: true<br/>  }<br/>})<br/>` |
| `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` | n/a | Not supported, use `external` |
| `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 `"inline"`, `"external"`, and `"none"` |
| `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` | n/a | Always true |
| `tsconfig` | n/a | Not supported |
| `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 the core functionality is implemented, and many third-party esbuild plugins work with Bun without modification.

<Note>
  Long term, we aim for feature parity with esbuild's API. If something doesn't work, file an issue to help us
  prioritize.
</Note>

Plugins in Bun and esbuild are defined with a builder object.

```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-1dd33a4e-farm-084c10c9-docs-lifecycle-supported-scripts/R-wx9Y3U966sjHmG/icons/typescript.svg?fit=max&auto=format&n=R-wx9Y3U966sjHmG&q=85&s=760430b8f5b281b582b200836ae7fe1d" theme={null}
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`. `initialOptions` is partially implemented: it's 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="https://mintcdn.com/bun-1dd33a4e-farm-084c10c9-docs-lifecycle-supported-scripts/R-wx9Y3U966sjHmG/icons/typescript.svg?fit=max&auto=format&n=R-wx9Y3U966sjHmG&q=85&s=760430b8f5b281b582b200836ae7fe1d" theme={null}
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

<Tabs>
  <Tab title="options">
    * 🟢 `filter`
    * 🟢 `namespace`
  </Tab>

  <Tab title="arguments">
    * 🟢 `path`
    * 🟢 `importer`
    * 🔴 `namespace`
    * 🔴 `resolveDir`
    * 🔴 `kind`
    * 🔴 `pluginData`
  </Tab>

  <Tab title="results">
    * 🟢 `namespace`
    * 🟢 `path`
    * 🔴 `errors`
    * 🔴 `external`
    * 🔴 `pluginData`
    * 🔴 `pluginName`
    * 🔴 `sideEffects`
    * 🔴 `suffix`
    * 🔴 `warnings`
    * 🔴 `watchDirs`
    * 🔴 `watchFiles`
  </Tab>
</Tabs>

### onLoad

<Tabs>
  <Tab title="options">
    * 🟢 `filter`
    * 🟢 `namespace`
  </Tab>

  <Tab title="arguments">
    * 🟢 `path`
    * 🔴 `namespace`
    * 🔴 `suffix`
    * 🔴 `pluginData`
  </Tab>

  <Tab title="results">
    * 🟢 `contents`
    * 🟢 `loader`
    * 🔴 `errors`
    * 🔴 `pluginData`
    * 🔴 `pluginName`
    * 🔴 `resolveDir`
    * 🔴 `warnings`
    * 🔴 `watchDirs`
    * 🔴 `watchFiles`
  </Tab>
</Tabs>
