--- title: Bundler description: Bun's fast native bundler for JavaScript, TypeScript, JSX, and more --- import Build from "/snippets/cli/build.mdx"; Use Bun's native bundler through the `bun build` CLI command or the `Bun.build()` JavaScript API. ### At a Glance - JS API: `await Bun.build({ entrypoints, outdir })` - CLI: `bun build --outdir ./out` - Watch: `--watch` for incremental rebuilds - Targets: `--target browser|bun|node` - Formats: `--format esm|cjs|iife` (experimental for cjs/iife) ```ts title="build.ts" icon="/icons/typescript.svg" await Bun.build({ entrypoints: ['./index.tsx'], outdir: './build', }); ``` ```bash terminal icon="terminal" bun build ./index.tsx --outdir ./build ``` It's fast. The following numbers are from esbuild's [three.js benchmark](https://github.com/oven-sh/bun/tree/main/bench/bundle). ## Why bundle? Bundlers solve several problems: - **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, so bundlers convert your 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 these must be converted into plain JavaScript and CSS before a browser can consume them. 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. The Bun bundler is not intended to replace `tsc` for typechecking or generating type declarations. ## Basic example Build your first bundle. You have the following two files, which implement a client-side rendered React app. ```tsx index.tsx icon="/icons/typescript.svg" import * as ReactDOM from "react-dom/client"; import { Component } from "./Component"; const root = ReactDOM.createRoot(document.getElementById("root")!); root.render(); ``` ```tsx Component.tsx icon="/icons/typescript.svg" export function Component(props: { message: string }) { return

{props.message}

; } ```
Here, `index.tsx` is the "entrypoint" to the application: the file the bundler starts from. Commonly, this is a script that performs some side effect, like starting a server or, in this case, initializing a React root. Because these files use TypeScript and JSX, the code must be bundled before it can be sent to the browser. To create the bundle: ```ts build.ts icon="/icons/typescript.svg" await Bun.build({ entrypoints: ["./index.tsx"], outdir: "./out", }); ``` ```bash terminal icon="terminal" bun build ./index.tsx --outdir ./out ``` For each file specified in `entrypoints`, Bun generates a new bundle and writes it to the `./out` directory (as resolved from the current working directory). After running the build, the file system looks like this: ```text title="file system" icon="folder-tree" . ├── index.tsx ├── Component.tsx └── out └── index.js ``` The contents of `out/index.js` look something like this: ```js title="out/index.js" icon="/icons/javascript.svg" // 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( "h1", { 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, ), ); ``` ## Watch mode Like the runtime and test runner, the bundler supports watch mode natively. ```bash terminal icon="terminal" bun build ./index.tsx --outdir ./out --watch ``` ## Content types Like the Bun runtime, the bundler supports a range of file types by default. The following table lists the bundler's standard "loaders". See [loaders](/bundler/loaders). | 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. Bun does not down-convert syntax; if you use recent ECMAScript syntax, it appears as-is in the bundled code. | | `.json` | JSON files are parsed and inlined into the bundle as a JavaScript object.

`js
import pkg from "./package.json";
pkg.name; // => "my-package"
` | | `.jsonc` | JSON with comments. Files are parsed and inlined into the bundle as a JavaScript object.

`js
import config from "./config.jsonc";
config.name; // => "my-config"
` | | `.toml` | TOML files are parsed and inlined into the bundle as a JavaScript object.

`js
import config from "./bunfig.toml";
config.logLevel; // => "debug"
` | | `.yaml` `.yml` | YAML files are parsed and inlined into the bundle as a JavaScript object.

`js
import config from "./config.yaml";
config.name; // => "my-app"
` | | `.txt` | The contents of the text file are read and inlined into the bundle as a string.

`js
import contents from "./file.txt";
console.log(contents); // => "Hello, world!"
` | | `.html` | HTML files are processed and any referenced assets (scripts, stylesheets, images) are bundled. | | `.css` | CSS files are bundled together into a single `.css` file in the output directory. | | `.node` `.wasm` | The Bun runtime supports these files, but the bundler treats them as assets. | ### Assets If the bundler encounters an import with an unrecognized extension, it treats the imported file as an external file. The bundler copies the referenced file as-is into `outdir` and resolves the import as a path to the file. ```ts Input icon="/icons/typescript.svg" // bundle entrypoint import logo from "./logo.svg"; console.log(logo); ``` ```ts Output icon="/icons/javascript.svg" // bundled output var logo = "./logo-a7305bdef.svg"; console.log(logo); ``` The exact behavior of the file loader also depends on [`naming`](#naming) and [`publicPath`](#publicpath). See [loaders](/bundler/loaders) for more on the file loader. ### Plugins Plugins can override or extend the behavior described in this table. See [loaders](/bundler/loaders). ## API ### entrypoints Required An array of paths corresponding to the entrypoints of your application. Bun generates one bundle per entrypoint. ```ts title="build.ts" icon="/icons/typescript.svg" const result = await Bun.build({ entrypoints: ["./index.ts"], }); // => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash terminal icon="terminal" bun build ./index.ts ``` ### files A map of file paths to their contents for in-memory bundling: bundle virtual files that don't exist on disk, or override the contents of files that do. This option is only available in the JavaScript API. You can provide file contents as a `string`, `Blob`, `TypedArray`, or `ArrayBuffer`. #### Bundle entirely from memory You can bundle code without any files on disk by providing all sources in `files`: ```ts title="build.ts" icon="/icons/typescript.svg" const result = await Bun.build({ entrypoints: ["/app/index.ts"], files: { "/app/index.ts": ` import { greet } from "./greet.ts"; console.log(greet("World")); `, "/app/greet.ts": ` export function greet(name: string) { return "Hello, " + name + "!"; } `, }, }); const output = await result.outputs[0].text(); console.log(output); ``` When all entrypoints are in the `files` map, Bun uses the current working directory as the root. #### Override files on disk In-memory files take priority over files on disk, so you can override specific files while keeping the rest of your codebase unchanged: ```ts title="build.ts" icon="/icons/typescript.svg" // Assume ./src/config.ts exists on disk with development settings await Bun.build({ entrypoints: ["./src/index.ts"], files: { // Override config.ts with production values "./src/config.ts": ` export const API_URL = "https://api.production.com"; export const DEBUG = false; `, }, outdir: "./dist", }); ``` #### Mix disk and virtual files Real files on disk can import virtual files, and virtual files can import real files: ```ts title="build.ts" icon="/icons/typescript.svg" // ./src/index.ts exists on disk and imports "./generated.ts" await Bun.build({ entrypoints: ["./src/index.ts"], files: { // Provide a virtual file that index.ts imports "./src/generated.ts": ` export const BUILD_ID = "${crypto.randomUUID()}"; export const BUILD_TIME = ${Date.now()}; `, }, outdir: "./dist", }); ``` Use this for code generation, injecting build-time constants, or testing with mock modules. ### outdir The directory where output files are written. ```ts title="build.ts" icon="/icons/typescript.svg" const result = await Bun.build({ entrypoints: ['./index.ts'], outdir: './out' }); // => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } ``` ```bash terminal icon="terminal" bun build ./index.ts --outdir ./out ``` If you don't pass `outdir` to the JavaScript API, Bun does not write bundled code to disk. It returns the bundled files in an array of `BuildArtifact` objects. These objects are Blobs with extra properties; see [Outputs](#outputs). ```ts title="build.ts" icon="/icons/typescript.svg" const result = await Bun.build({ entrypoints: ["./index.ts"], }); for (const res of result.outputs) { // Can be consumed as blobs await res.text(); // Bun sets 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` is the absolute path it was written to. ### target The intended execution environment for the bundle. ```ts title="build.ts" icon="/icons/typescript.svg" await Bun.build({ entrypoints: ['./index.ts'], outdir: './out', target: 'browser', // default }) ``` ```bash terminal icon="terminal" bun build ./index.ts --outdir ./out --target browser ``` Depending on the target, Bun applies different module resolution rules and optimizations. **Default.** For bundles that run in a browser. Prioritizes the `"browser"` export condition when resolving imports. Importing built-in modules like `node:events` or `node:path` works, but calling some functions, like `fs.readFile`, does not. For bundles that run in 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. Use this target for full-stack applications with build-time HTML imports, where server and client code are bundled together. All bundles generated with `target: "bun"` are marked with a `// @bun` pragma, which tells the Bun runtime that there's no need to re-transpile the file before execution. If any entrypoint contains a Bun shebang (`#!/usr/bin/env bun`), the bundler defaults to `target: "bun"` instead of `"browser"`. When you use `target: "bun"` and `format: "cjs"` together, the bundler adds the `// @bun @bun-cjs` pragma, and the CommonJS wrapper function is not compatible with Node.js. For bundles that run in Node.js. Prioritizes the `"node"` export condition when resolving imports. Bun does not polyfill the `Bun` global or the built-in `bun:*` modules. ### format Specifies the module format of the generated bundles. Bun defaults to `"esm"`, and provides experimental support for `"cjs"` and `"iife"`. #### format: "esm" - ES Module The default format. Supports ES Module syntax, including top-level await and `import.meta`. ```ts title="build.ts" icon="/icons/typescript.svg" await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', format: "esm", }) ``` ```bash terminal icon="terminal" bun build ./index.tsx --outdir ./out --format esm ``` To use ES Module syntax in browsers, set `format` to `"esm"` and load the bundle with a `