Bun's bundler API is inspired heavily by [esbuild](https://esbuild.github.io/). Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API.

There are a few behavioral differences to note.

- **Bundling by default**. Unlike esbuild, Bun _always bundles by default_. This is why the `--bundle` flag isn't necessary in the Bun example. To transpile each file individually, use [`Bun.Transpiler`](https://bun.sh/docs/api/transpiler).
- **It's just a bundler**. Unlike esbuild, Bun's bundler does not include a built-in development server or file watcher. It's just a bundler. The bundler is intended for use in conjunction with `Bun.serve` and other runtime APIs to achieve the same effect. As such, all options relating to HTTP/file watching are not applicable.

## Performance

With a performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's [three.js benchmark](https://github.com/oven-sh/bun/tree/main/bench/bundle).

{% image src="/images/bundler-speed.png" caption="Bundling 10 copies of three.js from scratch, with sourcemaps and minification" /%}

## CLI API

Bun and esbuild both provide a command-line interface.

```bash
$ esbuild <entrypoint> --outdir=out --bundle
$ bun build <entrypoint> --outdir=out
```

In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Other flags like `--outdir <path>` do accept an argument; these flags can be written as `--outdir out` or `--outdir=out`. Some flags like `--define` can be specified several times: `--define foo=bar --define bar=baz`.

{% table %}

- `esbuild`
- `bun build`

---

- `--bundle`
- n/a
- Bun always bundles, use `--no-bundle` to disable this behavior.

---

- `--define:K=V`
- `--define K=V`
- Small syntax difference; no colon.

  ```bash
  $ esbuild --define:foo=bar
  $ bun build --define foo=bar
  ```

---

- `--external:<pkg>`
- `--external <pkg>`
- Small syntax difference; no colon.

  ```bash
  $ esbuild --external:react
  $ bun build --external react
  ```

---

- `--format`
- `--format`
- Bun supports `"esm"` and `"cjs"` currently, but 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 [Bundler > Loaders](https://bun.sh/docs/bundler/loaders) for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented.

  The syntax for `--loader` is slightly different.

  ```bash
  $ esbuild app.ts --bundle --loader:.svg=text
  $ bun build app.ts --loader .svg:text
  ```

---

- `--minify`
- `--minify`
- No differences

---

- `--outdir`
- `--outdir`
- No differences

---

- `--outfile`
- `--outfile`

---

- `--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 at this time.

---

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

---

- `--entry-names`
- `--entry-naming`
- Renamed for consistency with `naming` in JS API

---

- `--global-name`
- n/a
- Not applicable, Bun does not support `iife` output at this time

---

- `--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 will use the `jsx` transform. Otherwise, it uses `jsxDEV`. For any to Bun 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 yet.

---

- `--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.

{% /table %}

## JavaScript API

{% table %}

- `esbuild.build()`
- `Bun.build()`

---

- `absWorkingDir`
- n/a
- Always set to `process.cwd()`

---

- `alias`
- n/a
- Not supported

---

- `allowOverwrite`
- n/a
- Always `false`

---

- `assetNames`
- `naming.asset`
- Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

  ```ts
  Bun.build({
    entrypoints: ["./index.tsx"],
    naming: {
      asset: "[name].[ext]",
    },
  });
  ```

---

- `banner`
- n/a
- Not supported

---

- `bundle`
- n/a
- Always `true`. Use [`Bun.Transpiler`](https://bun.sh/docs/api/transpiler) to transpile without bundling.

---

- `charset`
- n/a
- Not supported

---

- `chunkNames`
- `naming.chunk`
- Uses same templating syntax as esbuild, but `[ext]` must be included 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`
- 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 same templating syntax as esbuild, but `[ext]` must be included 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`
- n/a
- Not supported

---

- `format`
- `format`
- Only supports `"esm"` currently. 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 JS API, configure in `tsconfig.json`

---

- `jsxDev`
- `jsxDev`
- Not supported in JS API, configure in `tsconfig.json`

---

- `jsxFactory`
- `jsxFactory`
- Not supported in JS API, configure in `tsconfig.json`

---

- `jsxFragment`
- `jsxFragment`
- Not supported in JS API, configure in `tsconfig.json`

---

- `jsxImportSource`
- `jsxImportSource`
- Not supported in JS API, configure in `tsconfig.json`

---

- `jsxSideEffects`
- `jsxSideEffects`
- Not supported in 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 [Bundler > Loaders](https://bun.sh/docs/bundler/loaders) for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet 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

<!-- - `manifest`
- When `manifest` is `true`, the result of `Bun.build()` will contain a `manifest` property. The manifest is compatible with esbuild's metafile format. -->

---

- `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`
- 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 will work out of the box with Bun.

---

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

---

{% /table %}

## 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. Many third-party `esbuild` plugins will work out of the box with Bun.

{% callout %}
Long term, we aim for feature parity with esbuild's API, so if something doesn't work please file an issue to help us prioritize.

{% /callout %}

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

```ts
import type { BunPlugin } from "bun";

const myPlugin: BunPlugin = {
  name: "my-plugin",
  setup(builder) {
    // define plugin
  },
};
```

The `builder` object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use [`config`](https://bun.sh/docs/bundler/plugins) (same thing but with Bun's `BuildConfig` format) instead.

```ts
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
  name: "my-plugin",
  setup(builder) {
    builder.onResolve(
      {
        /* onResolve.options */
      },
      args => {
        return {
          /* onResolve.results */
        };
      },
    );
    builder.onLoad(
      {
        /* onLoad.options */
      },
      args => {
        return {
          /* onLoad.results */
        };
      },
    );
  },
};
```

### `onResolve`

#### `options`

{% table %}

- 🟢
- `filter`

---

- 🟢
- `namespace`

{% /table %}

#### `arguments`

{% table %}

- 🟢
- `path`

---

- 🟢
- `importer`

---

- 🔴
- `namespace`

---

- 🔴
- `resolveDir`

---

- 🔴
- `kind`

---

- 🔴
- `pluginData`

{% /table %}

#### `results`

{% table %}

- 🟢
- `namespace`

---

- 🟢
- `path`

---

- 🔴
- `errors`

---

- 🔴
- `external`

---

- 🔴
- `pluginData`

---

- 🔴
- `pluginName`

---

- 🔴
- `sideEffects`

---

- 🔴
- `suffix`

---

- 🔴
- `warnings`

---

- 🔴
- `watchDirs`

---

- 🔴
- `watchFiles`

{% /table %}

### `onLoad`

#### `options`

{% table %}

---

- 🟢
- `filter`

---

- 🟢
- `namespace`

{% /table %}

#### `arguments`

{% table %}

---

- 🟢
- `path`

---

- 🔴
- `namespace`

---

- 🔴
- `suffix`

---

- 🔴
- `pluginData`

{% /table %}

#### `results`

{% table %}

---

- 🟢
- `contents`

---

- 🟢
- `loader`

---

- 🔴
- `errors`

---

- 🔴
- `pluginData`

---

- 🔴
- `pluginName`

---

- 🔴
- `resolveDir`

---

- 🔴
- `warnings`

---

- 🔴
- `watchDirs`

---

- 🔴
- `watchFiles`

{% /table %}
