Skip to main content
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

terminal
# 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.
esbuildbun buildNotes
--bundlen/aBun always bundles; use --no-bundle to disable it.
--define:K=V--define K=VSmall syntax difference; no colon.
esbuild --define:foo=bar
bun build --define foo=bar
--external:<pkg>--external <pkg>Small syntax difference; no colon.
esbuild --external:react
bun build --external react
--format--formatBun supports "esm", "cjs", and "iife". esbuild defaults to "iife".
--loader:.ext=loader--loader .ext:loaderBun supports a different set of built-in loaders than esbuild; see 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--minifyNo differences
--outdir--outdirNo differences
--outfile--outfileNo differences
--packages--packagesNo differences
--platform--targetRenamed to --target for consistency with tsconfig. Does not support neutral.
--serven/aNot applicable
--sourcemap--sourcemapNo differences
--splitting--splittingNo differences
--targetn/aNot supported. Bun’s bundler performs no syntactic down-leveling.
--watch--watchNo differences
--allow-overwriten/aOverwriting is never allowed
--analyzen/aNot supported
--asset-names--asset-namingRenamed for consistency with naming in JS API
--banner--bannerOnly applies to js bundles
--footer--footerOnly applies to js bundles
--certfilen/aNot applicable
--charset=utf8n/aNot supported
--chunk-names--chunk-namingRenamed for consistency with naming in JS API
--colorn/aAlways enabled
--drop--drop
n/a--featureBun-specific. Enables feature flags for compile-time dead-code elimination through import { feature } from "bun:bundle"
--entry-names--entry-namingRenamed for consistency with naming in JS API
--global-namen/aNot supported
--ignore-annotations--ignore-dce-annotations
--injectn/aNot supported
--jsx--jsx-runtime <runtime>Supports "automatic" (uses jsx transform) and "classic" (uses React.createElement)
--jsx-devn/aBun 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-effectsn/aJSX is always assumed to be side-effect-free
--keep-names--keep-names
--keyfilen/aNot applicable
--legal-commentsn/aNot supported
--log-leveln/aNot supported. This can be set in bunfig.toml as logLevel.
--log-limitn/aNot supported
--log-override:X=Yn/aNot supported
--main-fieldsn/aNot supported
--mangle-cachen/aNot supported
--mangle-propsn/aNot supported
--mangle-quotedn/aNot supported
--metafile--metafile
--minify-whitespace--minify-whitespace
--minify-identifiers--minify-identifiers
--minify-syntax--minify-syntax
--out-extensionn/aNot supported
--outbase--root
--preserve-symlinksn/aNot supported
--public-path--public-path
--puren/aNot supported
--reserve-propsn/aNot supported
--resolve-extensionsn/aNot supported
--servedirn/aNot applicable
--source-rootn/aNot supported
--sourcefilen/aNot supported. Bun does not support stdin input.
--sourcemap--sourcemapNo differences
--sources-contentn/aNot supported
--supportedn/aNot supported
--tree-shakingn/aAlways true
--tsconfig--tsconfig-override
--versionn/aRun bun --version to see the version of Bun.

JavaScript API

esbuild.build()Bun.build()Notes
absWorkingDirn/aAlways set to process.cwd()
aliasn/aNot supported
allowOverwriten/aAlways false
assetNamesnaming.assetUses the same templating syntax as esbuild, but [ext] must be included explicitly.

ts<br/>Bun.build({<br/> entrypoints: ["./index.tsx"],<br/> naming: {<br/> asset: "[name].[ext]",<br/> },<br/>});<br/>
bannerbannerOnly applies to js bundles
bundlen/aAlways true. Use Bun.Transpiler to transpile without bundling.
charsetn/aNot supported
chunkNamesnaming.chunkUses the same templating syntax as esbuild, but [ext] must be included explicitly.

ts<br/>Bun.build({<br/> entrypoints: ["./index.tsx"],<br/> naming: {<br/> chunk: "[name].[ext]",<br/> },<br/>});<br/>
colorn/aBun returns logs in the logs property of the build result.
conditionsconditionsNo differences
definedefine
dropdrop
entryNamesnaming or naming.entryBun 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.

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/>
entryPointsentrypointsCapitalization difference
externalexternalNo differences
footerfooterOnly applies to js bundles
formatformatSupports "esm", "cjs", and "iife".
globalNamen/aNot supported
ignoreAnnotationsignoreDCEAnnotations
injectn/aNot supported
jsxjsx.runtimeSupports "automatic" and "classic"
jsxDevjsx.development
jsxFactoryjsx.factory
jsxFragmentjsx.fragment
jsxImportSourcejsx.importSource
jsxSideEffectsjsx.sideEffects
keepNamesminify.keepNames
legalCommentsn/aNot supported
loaderloaderBun supports a different set of built-in loaders than esbuild; see loaders. The esbuild loaders dataurl, binary, base64, copy, and empty are not implemented.
logLeveln/aNot supported
logLimitn/aNot supported
logOverriden/aNot supported
mainFieldsn/aNot supported
mangleCachen/aNot supported
manglePropsn/aNot supported
mangleQuotedn/aNot supported
metafilemetafile
minifyminifyIn Bun, minify can be a boolean or an object.

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/>
minifyIdentifiersminify.identifiersSee minify
minifySyntaxminify.syntaxSee minify
minifyWhitespaceminify.whitespaceSee minify
nodePathsn/aNot supported
outExtensionn/aNot supported
outbaserootDifferent name
outdiroutdirNo differences
outfileoutfileNo differences
packagespackagesNo differences
platformtargetSupports "bun", "node" and "browser" (the default). Does not support "neutral".
pluginspluginsBun’s plugin API is a subset of esbuild’s. Some esbuild plugins work with Bun without modification.
preserveSymlinksn/aNot supported
publicPathpublicPathNo differences
puren/aNot supported
reservePropsn/aNot supported
resolveExtensionsn/aNot supported
sourceRootn/aNot supported
sourcemapsourcemapSupports "none", "linked", "inline", and "external"
sourcesContentn/aNot supported
splittingsplittingNo differences
stdinn/aNot supported
supportedn/aNot supported
targetn/aNo support for syntax downleveling
treeShakingtreeShakingDefaults to true
tsconfigtsconfig
writen/aSet 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.
Long term, we aim for feature parity with esbuild’s API. If something doesn’t work, file an issue to help us prioritize.
Plugins in Bun and esbuild are defined with a builder object.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmyPlugin.ts
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.
https://mintcdn.com/bun-1dd33a4e/JUhaF6Mf68z_zHyy/icons/typescript.svg?fit=max&auto=format&n=JUhaF6Mf68z_zHyy&q=85&s=7ac549adaea8d5487d8fbd58cc3ea35bmyPlugin.ts
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

onLoad

  • 🟢 filter
  • 🟢 namespace