Skip to main content
Bun’s bundler API is heavily inspired by esbuild. Migrating from esbuild to Bun’s bundler should be relatively straightforward. This document briefly explains why you might consider migrating to Bun’s bundler and provides a side-by-side API reference for those already familiar with esbuild. There are a few behavioral differences to be aware of.
Bundling by default. Unlike esbuild, Bun always bundles by default. That’s why the --bundle flag doesn’t appear in Bun examples. To transpile each file individually, use Bun.Transpiler.
It’s just a bundler. Unlike esbuild, Bun’s bundler does not include a built-in dev server or file watcher. It’s just a bundler. The bundler is designed to be used with Bun.serve and other runtime APIs to achieve the same effect. As such, all HTTP/file-watching related options are not applicable.

Performance

With a performance-first API and heavily optimized Zig-based JS/TS parser, Bun’s bundler is 1.75x faster than esbuild on esbuild’s three.js benchmark.
Bundle 10 copies of three.js from scratch with source maps and minification

Why migrate?

Here are some reasons you might want to migrate from esbuild to Bun’s bundler:

Speed

Bun’s bundler is significantly faster than esbuild, especially for large projects. The Zig-based implementation and careful optimizations make builds complete faster.

Native features

Bun includes native support for:
  • TypeScript and JSX: No configuration needed
  • CSS bundling: Built-in CSS processing with modern features
  • HTML processing: Bundle complete web applications
  • Macros: Run code at build time
  • Framework features: React Fast Refresh, server components

Unified toolchain

Bun provides a complete JavaScript runtime and toolkit:
  • Bundler
  • Runtime
  • Test runner
  • Package manager
Using Bun means fewer tools to install, configure, and maintain.

Modern defaults

Bun uses modern defaults:
  • ESM by default
  • Modern JavaScript syntax
  • Fast native loaders for common file types

CLI API

Both Bun and esbuild provide command-line interfaces.
# esbuild
esbuild <entrypoint> --outdir=out --bundle

# bun
bun build <entrypoint> --outdir=out
In Bun’s CLI, simple boolean flags like --minify don’t accept arguments. Other flags with arguments like --outdir <path> accept arguments; these flags can be written as --outdir out or --outdir=out. Some flags like --define can be specified multiple times: --define foo=bar --define bar=baz.
esbuildbun buildNotes
--bundleN/ABun always bundles; use --no-bundle to disable.
--define:K=V--define K=VMinor syntax difference; no colon.
esbuild --define:foo=bar
bun build --define foo=bar
--external:<pkg>--external <pkg>Minor syntax difference; no colon.
esbuild --external:react
bun build --external react
--format--formatBun currently supports "esm" and "cjs", with more module formats planned. esbuild defaults to "iife".
--loader:.ext=loader--loader .ext:loaderBun’s set of built-in loaders differs from esbuild’s; see Bundler > Loaders for complete reference. esbuild’s dataurl, binary, base64, copy, and empty loaders are not yet implemented.

The --loader syntax is slightly different.
esbuild app.ts --bundle --loader:.svg=text
bun build app.ts --loader .svg:text
--minify--minifyNo difference
--outdir--outdirNo difference
--outfile--outfileNo difference
--packages--packagesNo difference
--platform--targetRenamed to --target for consistency with tsconfig. Does not support neutral.
--serveN/ANot applicable
--sourcemap--sourcemapNo difference
--splitting--splittingNo difference
--targetN/ANot supported. Bun’s bundler currently doesn’t support syntax downleveling.
--watch--watchNo difference

Additional flags

esbuildbun buildNotes
--allow-overwriteN/ANot allowed
--analyzeN/ANot supported
--asset-names--asset-namingRenamed for consistency with JS API
--banner--bannerOnly applies to js bundles
--footer--footerOnly applies to js bundles
--charset=utf8N/ANot supported
--chunk-names--chunk-namingRenamed for consistency with JS API
--drop--drop
N/A--featureBun-specific. Feature flags for compile-time dead code elimination via import { feature } from "bun:bundle"
--entry-names--entry-namingRenamed for consistency with JS API
--global-nameN/ANot applicable; Bun doesn’t currently support iife output
--ignore-annotations--ignore-dce-annotations
--injectN/ANot supported
--jsx--jsx-runtime <runtime>Supports "automatic" (jsx transform) and "classic" (React.createElement)
--jsx-devN/ABun reads defaults from tsconfig.json’s compilerOptions.jsx. If the value is "react-jsx" or the environment variable 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-effectsN/AJSX is always assumed to be side-effect free
--keep-namesN/ANot supported
--legal-commentsN/ANot supported
--log-levelN/ANot supported; can be set via bunfig.toml using logLevel
--main-fieldsN/ANot supported
--metafile--metafileBun supports both JSON and Markdown metafiles
--minify-whitespace--minify-whitespace
--minify-identifiers--minify-identifiers
--minify-syntax--minify-syntax
--out-extensionN/ANot supported
--outbase--root
--public-path--public-path
--sourcemap--sourcemapNo difference
--tsconfig--tsconfig-override

JavaScript API

esbuild.build()Bun.build()Notes
absWorkingDirN/AAlways set to process.cwd()
aliasN/ANot supported
allowOverwriteN/AAlways false
assetNamesnaming.assetUses the same template syntax as esbuild, but must explicitly include [ext].
bannerN/ANot supported
bundleN/AAlways on. To transpile without bundling, use Bun.Transpiler.
charsetN/ANot supported
chunkNamesnaming.chunkUses the same template syntax as esbuild, but must explicitly include [ext].
colorN/ABun returns log messages in the logs property of the build result.
conditionsN/ANot supported. Export condition priority is determined by target.
definedefine
dropN/ANot supported
entryNamesnaming or naming.entryBun supports a naming key that can be either a string or an object. Uses the same template syntax as esbuild, but must explicitly include [ext].
entryPointsentrypointsCasing difference
externalexternalNo difference
footerN/ANot supported
formatformatCurrently only supports "esm". Plans to support "cjs" and "iife".
globalNameN/ANot supported
ignoreAnnotationsN/ANot supported
injectN/ANot supported
jsxjsxNot supported in JS API; configure via tsconfig.json
jsxDevjsxDevNot supported in JS API; configure via tsconfig.json
jsxFactoryjsxFactoryNot supported in JS API; configure via tsconfig.json
jsxFragmentjsxFragmentNot supported in JS API; configure via tsconfig.json
jsxImportSourcejsxImportSourceNot supported in JS API; configure via tsconfig.json
jsxSideEffectsjsxSideEffectsNot supported in JS API; configure via tsconfig.json
keepNamesN/ANot supported
legalCommentsN/ANot supported
loaderloaderBun’s set of built-in loaders differs from esbuild’s; see Bundler > Loaders for complete reference.
logLevelN/ANot supported
logLimitN/ANot supported
logOverrideN/ANot supported
mainFieldsN/ANot supported
mangleCacheN/ANot supported
manglePropsN/ANot supported
mangleQuotedN/ANot supported
metafilemetafileBun supports boolean, string (path), or object (with json/markdown paths)
minifyminifyIn Bun, minify can be a boolean or object with granular control.
minifyIdentifiersminify.identifiers
minifySyntaxminify.syntax
minifyWhitespaceminify.whitespace
nodePathsN/ANot supported
outExtensionN/ANot supported
outbaseroot
outdiroutdir
outfileoutfile
packagespackages
platformtarget
pluginspluginsBun’s plugin API is similar but not identical to esbuild’s
preserveSymlinksN/ANot supported
publicPathpublicPath
pureN/ANot supported
resolveExtensionsN/ANot supported
sourceRootN/ANot supported
sourcemapsourcemapNo difference
sourcesContentN/ANot supported
splittingsplitting
stdinN/ANot supported; Bun doesn’t currently support stdin input
supportedN/ANot supported
targetN/ANot supported; Bun doesn’t do syntax downleveling
treeShakingN/AAlways enabled
tsconfigtsconfig
writeN/AAlways true when outdir is specified

Plugin API

Bun’s plugin API is similar to esbuild’s but not identical. Both use onLoad and onResolve hooks, but the exact API differs:

esbuild

const plugin = {
  name: 'example',
  setup(build) {
    build.onResolve({ filter: /.*/ }, args => {
      return { path: args.path };
    });
    
    build.onLoad({ filter: /.*/ }, args => {
      return { contents: 'export default {}' };
    });
  }
};

Bun

const plugin: BunPlugin = {
  name: 'example',
  setup(build) {
    build.onResolve({ filter: /.*/ }, args => {
      return { path: args.path };
    });
    
    build.onLoad({ filter: /.*/ }, args => {
      return { contents: 'export default {}', loader: 'js' };
    });
  }
};
Key differences:
  • Bun requires explicit loader specification in onLoad
  • Bun supports additional lifecycle hooks: onStart, onEnd, onBeforeParse
  • Bun’s plugin system is shared between runtime and bundler
See Bundler > Plugins for complete documentation.

Migration checklist

  1. Update your build script
    • Change esbuild to bun build in CLI commands
    • Change esbuild.build() to Bun.build() in JavaScript
  2. Remove --bundle flag
    • Bun bundles by default
    • Add --no-bundle if you need transpilation only
  3. Update loader syntax
    • Change --loader:.svg=text to --loader .svg:text
  4. Update naming options
    • assetNamesnaming.asset
    • chunkNamesnaming.chunk
    • entryNamesnaming.entry
  5. Update platform option
    • --platform--target
  6. Check unsupported features
    • Remove --serve (use Bun.serve instead)
    • Remove --target (syntax downleveling not supported)
    • Remove --inject if used
  7. Update plugins
    • Review plugin API differences
    • Add explicit loader to onLoad returns
    • Test plugin behavior
  8. Test your build
    • Run the new build command
    • Verify output files
    • Test in browsers/environments

Example migration

Before (esbuild)

package.json
{
  "scripts": {
    "build": "esbuild src/index.ts --bundle --outdir=dist --minify --sourcemap"
  }
}

After (Bun)

package.json
{
  "scripts": {
    "build": "bun build src/index.ts --outdir=dist --minify --sourcemap"
  }
}
That’s it! Most esbuild projects can be migrated with minimal changes.

Getting help

If you encounter issues during migration:
  1. Check the Bun documentation
  2. Search GitHub issues
  3. Ask in the Bun Discord
  4. File a bug report if you find a problem

Build docs developers (and LLMs) love