Bun's fast native bundler is now in beta. It can be used via the `bun build` CLI command or the `Bun.build()` JavaScript API. {% codetabs group="a" %} ```ts#JavaScript await Bun.build({ entrypoints: ['./index.tsx'], outdir: './build', }); ``` ```sh#CLI $ bun build ./index.tsx --outdir ./build ``` {% /codetabs %} It's fast. The numbers below represent performance 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" /%} ## Why bundle? The bundler is a key piece of infrastructure in the JavaScript ecosystem. As a brief overview of why bundling is so important: - **Reducing HTTP requests.** A single package in `node_modules` may consist of hundreds of files, and large applications may have dozens of such dependencies. Loading each of these files with a separate HTTP request becomes untenable very quickly, so bundlers are used to convert our application source code into a smaller number of self-contained "bundles" that can be loaded with a single request. - **Code transforms.** Modern apps are commonly built with languages or tools like TypeScript, JSX, and CSS modules, all of which must be converted into plain JavaScript and CSS before they can be consumed by a browser. The bundler is the natural place to configure these transformations. - **Framework features.** Frameworks rely on bundler plugins & code transformations to implement common patterns like file-system routing, client-server code co-location (think `getServerSideProps` or Remix loaders), and server components. - **Full-stack Applications.** Bun's bundler can handle both server and client code in a single command, enabling optimized production builds and single-file executables. With build-time HTML imports, you can bundle your entire application — frontend assets and backend server — into a single deployable unit. Let's jump into the bundler API. {% callout %} Note that the Bun bundler is not intended to replace `tsc` for typechecking or generating type declarations. {% /callout %} ## Basic example Let's build our first bundle. You have the following two files, which implement a simple client-side rendered React app. {% codetabs %} ```tsx#./index.tsx import * as ReactDOM from 'react-dom/client'; import {Component} from "./Component" const root = ReactDOM.createRoot(document.getElementById('root')!); root.render() ``` ```tsx#./Component.tsx export function Component(props: {message: string}) { return

{props.message}

} ``` {% /codetabs %} Here, `index.tsx` is the "entrypoint" to our application. Commonly, this will be a script that performs some _side effect_, like starting a server or—in this case—initializing a React root. Because we're using TypeScript & JSX, we need to bundle our code before it can be sent to the browser. To create our bundle: {% codetabs group="a" %} ```ts#JavaScript await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', }) ``` ```bash#CLI $ bun build ./index.tsx --outdir ./out ``` {% /codetabs %} For each file specified in `entrypoints`, Bun will generate a new bundle. This bundle will be written to disk in the `./out` directory (as resolved from the current working directory). After running the build, the file system looks like this: ```ts . ├── index.tsx ├── Component.tsx └── out └── index.js ``` The contents of `out/index.js` will look something like this: ```js#out/index.js // ... // ~20k lines of code // including the contents of `react-dom/client` and all its dependencies // this is where the $jsxDEV and $createRoot functions are defined // Component.tsx function Component(props) { return $jsxDEV("p", { children: props.message }, undefined, false, undefined, this); } // index.tsx var rootNode = document.getElementById("root"); var root = $createRoot(rootNode); root.render($jsxDEV(Component, { message: "Sup!" }, undefined, false, undefined, this)); ``` {% details summary="Tutorial: Run this file in your browser" %} We can load this file in the browser to see our app in action. Create an `index.html` file in the `out` directory: ```bash $ touch out/index.html ``` Then paste the following contents into it: ```html
``` Then spin up a static file server serving the `out` directory: ```bash $ bunx serve out ``` Visit `http://localhost:5000` to see your bundled app in action. {% /details %} ## Watch mode Like the runtime and test runner, the bundler supports watch mode natively. ```sh $ bun build ./index.tsx --outdir ./out --watch ``` ## Content types Like the Bun runtime, the bundler supports an array of file types out of the box. The following table breaks down the bundler's set of standard "loaders". Refer to [Bundler > File types](https://bun.com/docs/runtime/loaders) for full documentation. {% table %} - Extensions - Details --- - `.js` `.jsx`, `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` - Uses Bun's built-in transpiler to parse the file and transpile TypeScript/JSX syntax to vanilla JavaScript. The bundler executes a set of default transforms including dead code elimination and tree shaking. At the moment Bun does not attempt to down-convert syntax; if you use recently ECMAScript syntax, that will be reflected in the bundled code. --- - `.json` - JSON files are parsed and inlined into the bundle as a JavaScript object. ```ts import pkg from "./package.json"; pkg.name; // => "my-package" ``` --- - `.toml` - TOML files are parsed and inlined into the bundle as a JavaScript object. ```ts import config from "./bunfig.toml"; config.logLevel; // => "debug" ``` --- - `.txt` - The contents of the text file are read and inlined into the bundle as a string. ```ts import contents from "./file.txt"; console.log(contents); // => "Hello, world!" ``` --- - `.node` `.wasm` - These files are supported by the Bun runtime, but during bundling they are treated as [assets](#assets). {% /table %} ### Assets If the bundler encounters an import with an unrecognized extension, it treats the imported file as an _external file_. The referenced file is copied as-is into `outdir`, and the import is resolved as a _path_ to the file. {% codetabs %} ```ts#Input // bundle entrypoint import logo from "./logo.svg"; console.log(logo); ``` ```ts#Output // bundled output var logo = "./logo-ab237dfe.svg"; console.log(logo); ``` {% /codetabs %} {% callout %} The exact behavior of the file loader is also impacted by [`naming`](#naming) and [`publicPath`](#publicpath). {% /callout %} Refer to the [Bundler > Loaders](https://bun.com/docs/bundler/loaders#file) page for more complete documentation on the file loader. ### Plugins The behavior described in this table can be overridden or extended with [plugins](https://bun.com/docs/bundler/plugins). Refer to the [Bundler > Loaders](https://bun.com/docs/bundler/plugins) page for complete documentation. ## API ### `entrypoints` **Required.** An array of paths corresponding to the entrypoints of our application. One bundle will be generated for each entrypoint. {% codetabs group="a" %} ```ts#JavaScript const result = await Bun.build({ entrypoints: ["./index.ts"], }); // => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash#CLI $ bun build --entrypoints ./index.ts # the bundle will be printed to stdout # ``` {% /codetabs %} ### `outdir` The directory where output files will be written. {% codetabs group="a" %} ```ts#JavaScript const result = await Bun.build({ entrypoints: ['./index.ts'], outdir: './out' }); // => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash#CLI $ bun build --entrypoints ./index.ts --outdir ./out # a summary of bundled files will be printed to stdout ``` {% /codetabs %} If `outdir` is not passed to the JavaScript API, bundled code will not be written to disk. Bundled files are returned in an array of `BuildArtifact` objects. These objects are Blobs with extra properties; see [Outputs](#outputs) for complete documentation. ```ts const result = await Bun.build({ entrypoints: ["./index.ts"], }); for (const res of result.outputs) { // Can be consumed as blobs await res.text(); // Bun will set Content-Type and Etag headers new Response(res); // Can be written manually, but you should use `outdir` in this case. Bun.write(path.join("out", res.path), res); } ``` When `outdir` is set, the `path` property on a `BuildArtifact` will be the absolute path to where it was written to. ### `target` The intended execution environment for the bundle. {% codetabs group="a" %} ```ts#JavaScript await Bun.build({ entrypoints: ['./index.ts'], outdir: './out', target: 'browser', // default }) ``` ```bash#CLI $ bun build --entrypoints ./index.ts --outdir ./out --target browser ``` {% /codetabs %} Depending on the target, Bun will apply different module resolution rules and optimizations. {% table %} --- - `browser` - _Default._ For generating bundles that are intended for execution by a browser. Prioritizes the `"browser"` export condition when resolving imports. Importing any built-in modules, like `node:events` or `node:path` will work, but calling some functions, like `fs.readFile` will not work. --- - `bun` - For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance. This is the target to use for building full-stack applications with build-time HTML imports, where both server and client code are bundled together. All bundles generated with `target: "bun"` are marked with a special `// @bun` pragma, which indicates to the Bun runtime that there's no need to re-transpile the file before execution. If any entrypoints contains a Bun shebang (`#!/usr/bin/env bun`) the bundler will default to `target: "bun"` instead of `"browser"`. When using `target: "bun"` and `format: "cjs"` together, the `// @bun @bun-cjs` pragma is added and the CommonJS wrapper function is not compatible with Node.js. --- - `node` - For generating bundles that are intended to be run by Node.js. Prioritizes the `"node"` export condition when resolving imports, and outputs `.mjs`. In the future, this will automatically polyfill the `Bun` global and other built-in `bun:*` modules, though this is not yet implemented. {% /table %} ### `format` Specifies the module format to be used in the generated bundles. Bun defaults to `"esm"`, and provides experimental support for `"cjs"` and `"iife"`. #### `format: "esm"` - ES Module This is the default format, which supports ES Module syntax including top-level `await`, import.meta, and more. {% codetabs %} ```ts#JavaScript await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', format: "esm", }) ``` ```bash#CLI $ bun build ./index.tsx --outdir ./out --format esm ``` {% /codetabs %} To use ES Module syntax in browsers, set `format` to `"esm"` and make sure your `