# esbuild User Guide

esbuild is an extremely fast bundler and minifier for JavaScript, TypeScript, JSX, and CSS. It reads your source files, resolves their imports, transforms modern syntax down to the target you choose, and writes optimized output — often **10–100× faster** than other bundlers because the whole engine is written in Go and runs as compiled native code with heavy parallelism.

You can use esbuild four ways: as a **command-line tool**, as a **JavaScript/TypeScript library** (Node.js), as a **browser library** (via WebAssembly), and as a **Go library**. This guide covers everyday usage of all four, with copy-paste-runnable examples for every important option.

> [!NOTE]
> This guide documents esbuild **v0.28.0**. esbuild has not yet reached `1.0.0`, so minor versions (`0.x`) may contain breaking changes. Pin an exact version in production (see [Installation](#2-installation)).

---

## 1. Quick Start

The fastest path from zero to a bundled file.

=== "npm (Node.js)"

    ```bash
    # Install esbuild as a dev dependency
    npm install --save-dev esbuild

    # Bundle and minify an app into a single file
    npx esbuild app.js --bundle --minify --outfile=out.js
    ```

=== "CLI (global)"

    ```bash
    npm install --global esbuild
    esbuild app.js --bundle --minify --outfile=out.js
    ```

=== "Go"

    ```go
    package main

    import (
        "os"
        "github.com/evanw/esbuild/pkg/api"
    )

    func main() {
        result := api.Build(api.BuildOptions{
            EntryPoints: []string{"app.js"},
            Outfile:     "out.js",
            Bundle:      true,
            MinifyWhitespace:  true,
            MinifyIdentifiers: true,
            MinifySyntax:      true,
            Write:    true,
            LogLevel: api.LogLevelInfo,
        })
        if len(result.Errors) > 0 {
            os.Exit(1)
        }
    }
    ```

Running the CLI command above prints a one-line build summary:

```text
  out.js  1.2kb

⚡ Done in 6ms
```

That is the whole loop: point esbuild at one or more **entry points**, tell it to `--bundle`, and choose where the output goes with `--outfile` (single file) or `--outdir` (multiple files).

> [!TIP]
> Use `npx esbuild` (or a `package.json` script) instead of a global install. It guarantees every developer and CI machine uses the exact version pinned in your `package.json`.

---

## 2. Installation

### Requirements

| Requirement | Detail |
|---|---|
| Node.js | **18 or newer** (for the `esbuild` and `esbuild-wasm` npm packages) |
| Platform | Native binaries ship for Windows, macOS, Linux, FreeBSD, OpenBSD, NetBSD, Android, illumos/SunOS, AIX, and more, across `x64`, `arm64`, `arm`, `ia32`, `ppc64`, `s390x`, `riscv64`, `loong64`, `mips64el` |
| Go | **Only** needed if you use the Go library API or build esbuild from source (not needed for the CLI or JS API) |

> [!IMPORTANT]
> The `esbuild` npm package contains **no binary itself**. At install time it pulls a small platform-specific package (e.g. `@esbuild/linux-x64`, `@esbuild/win32-x64`, `@esbuild/darwin-arm64`) through npm's `optionalDependencies` feature. This is why flags like `--no-optional` break the install — see [Troubleshooting](#9-troubleshooting).

### Install methods

=== "npm"

    ```bash
    npm install --save-dev esbuild
    ```

=== "pnpm"

    ```bash
    pnpm add --save-dev esbuild
    ```

=== "Yarn"

    ```bash
    yarn add --dev esbuild
    ```

=== "Deno"

    ```ts
    // Import directly from a URL — no install step
    import * as esbuild from "https://deno.land/x/esbuild/mod.js"
    ```

=== "Browser (WASM)"

    ```bash
    npm install --save-dev esbuild-wasm
    ```

### Verify the installation

```bash
npx esbuild --version
```

```text
0.28.0
```

> [!NOTE]
> The version printed by `--version` **must** match the version of the `esbuild` package. esbuild deliberately checks this at startup, because a mismatch almost always means a broken or partially-copied install. If they differ, reinstall.

### Pinning a version

Because esbuild is pre-1.0, pin an exact version (no `^` or `~`) in production:

```json
{
  "devDependencies": {
    "esbuild": "0.28.0"
  }
}
```

---

## 3. Core Concepts (the 90% you need)

esbuild does two fundamental operations. Everything else is an option on one of them.

| Operation | What it does | CLI | JS API | Go API |
|---|---|---|---|---|
| **Build** | Reads files from disk, resolves imports, writes output files | `esbuild <entry>` | `build()` / `context()` | `api.Build()` |
| **Transform** | Transforms a single in-memory string (no file system, no imports) | `esbuild < input` | `transform()` | `api.Transform()` |

Use **build** when you have entry-point files on disk and want bundling, code splitting, or multiple outputs. Use **transform** when you have a single chunk of source (for example, the contents of one file already in memory) and just want to convert or minify it.

The most common options, in plain terms:

| Option | CLI flag | What it controls |
|---|---|---|
| Bundle | `--bundle` | Inline all imported dependencies into the output (off by default) |
| Output file | `--outfile=out.js` | Where a single-file build is written |
| Output dir | `--outdir=dist` | Where a multi-file build is written |
| Minify | `--minify` | Shrink output (whitespace + identifiers + syntax) |
| Source map | `--sourcemap` | Emit a `.map` for debugging original source |
| Target | `--target=es2017` | Lowest JS syntax level / engine to support |
| Format | `--format=esm` | Module format of the output (`iife`, `cjs`, `esm`) |
| Platform | `--platform=node` | Resolution/defaults for `browser`, `node`, or `neutral` |
| Loader | `--loader:.png=dataurl` | How to interpret a given file extension |

> [!TIP]
> `--bundle` is **opt-in**. Without it, esbuild transforms each entry point independently and leaves `import`/`require` statements in place. If your output still has bare `import "./foo"` lines, you forgot `--bundle`.

---

## 4. Everyday Usage

### 4.1 Bundling an application

Combine an entry point and everything it imports into one file:

```bash
esbuild src/main.ts --bundle --outfile=dist/app.js
```

```text
  dist/app.js  142.7kb

⚡ Done in 11ms
```

For multiple entry points, use `--outdir` instead of `--outfile`:

```bash
esbuild src/home.ts src/admin.ts --bundle --outdir=dist
```

### 4.2 Minifying

```bash
esbuild app.js --minify --outfile=app.min.js
```

`--minify` is a shortcut that enables three independent transforms. You can turn them on individually:

| Flag | Effect |
|---|---|
| `--minify-whitespace` | Removes spaces, newlines, and indentation |
| `--minify-identifiers` | Renames local variables to short names |
| `--minify-syntax` | Rewrites code into equivalent shorter forms (e.g. `a ? b : c`) |

```bash
# Minify whitespace only — keeps readable variable names
esbuild app.js --minify-whitespace --outfile=app.js
```

### 4.3 Transforming TypeScript or JSX (no bundling)

esbuild strips TypeScript types and converts JSX without a separate compiler. Pipe a file through stdin:

```bash
esbuild --loader=ts < input.ts > output.js
```

> [!IMPORTANT]
> esbuild **does not type-check**. It removes type annotations and emits JavaScript regardless of type errors. Run `tsc --noEmit` separately if you want type checking. This is a deliberate speed trade-off, not a bug.

### 4.4 Source maps

```bash
esbuild app.js --bundle --sourcemap --outfile=out.js
```

The `--sourcemap` flag accepts several modes:

| Mode | Flag | Result |
|---|---|---|
| Linked | `--sourcemap` | Writes `out.js.map` and adds a `//# sourceMappingURL=` comment |
| External | `--sourcemap=external` | Writes `out.js.map` but adds **no** comment |
| Inline | `--sourcemap=inline` | Embeds the map as a base64 data URL inside `out.js` |
| Both | `--sourcemap=both` | Inline map **and** a separate `.map` file |

### 4.5 Choosing a target

Tell esbuild the oldest environment you must support, and it lowers newer syntax accordingly:

```bash
# Support these specific engine versions
esbuild app.js --bundle --target=es2017,chrome58,firefox57,safari11,node12 --outfile=out.js
```

Targets are either an ECMAScript version (`es5`, `es6`/`es2015`, `es2016` … `es2025`, `esnext`) or an `engineVERSION` pair (e.g. `chrome58`, `node10`, `safari11`, `ie9`, `edge16`, `opera45`). The default is `esnext` (assume everything is supported).

> [!WARNING]
> Some modern features (for example, top-level `await`) **cannot** be lowered to very old targets. esbuild will report an error rather than emit incorrect code. Either raise your target or avoid the feature.

### 4.6 Defining global constants

Replace identifiers at parse time — useful for dead-code elimination of debug branches:

```bash
esbuild app.js --bundle --define:DEBUG=false --define:process.env.NODE_ENV=\"production\" --outfile=out.js
```

> [!NOTE]
> Replacement values are **raw expressions**, not strings. To substitute a string literal you must include the quotes: `--define:VERSION=\"1.0\"`. To substitute the boolean `true`, write `--define:RELEASE=true`.

### 4.7 Marking packages as external

Exclude dependencies from the bundle (for example, Node built-ins or packages you ship separately):

```bash
# Don't bundle these — leave the import/require in place
esbuild app.js --bundle --platform=node --external:fsevents --external:*.node --outfile=out.js
```

To keep **all** `node_modules` packages external (common for Node libraries):

```bash
esbuild app.js --bundle --packages=external --outfile=out.js
```

### 4.8 Watch mode

Rebuild automatically when files change. Watch mode is available through the CLI and the JS `context()` API:

```bash
esbuild app.js --bundle --outfile=out.js --watch
```

```text
[watch] build finished, watching for changes...
```

> [!TIP]
> CLI watch mode stops automatically when stdin is closed (so it never outlives its parent process). To keep watching even after stdin closes — for example inside a short-lived VM — use `--watch=forever`.

### 4.9 Local development server

esbuild ships a built-in static server with live rebuilds:

```bash
esbuild app.js --bundle --servedir=www --outdir=www/js
```

```text
 > Local:   http://127.0.0.1:8000/
```

| Flag | Purpose |
|---|---|
| `--serve=8000` | Listen on a specific port (and optionally `host:port`) |
| `--servedir=www` | Serve static files from this directory in addition to build output |
| `--serve-fallback=index.html` | Serve this page when a request matches no file (SPA routing) |
| `--certfile=` / `--keyfile=` | Serve over HTTPS with your TLS certificate and key |
| `--cors-origin=` | Allow cross-origin requests from the given origin(s) |

> [!CAUTION]
> The development server is for **local development only**. It is not a hardened production web server — do not expose it to the public internet. Serve your built static files with a real web server in production.

---

## 5. Common Tasks (recipes)

### Bundle a React app for the browser

```bash
esbuild src/index.jsx --bundle --minify --sourcemap \
  --target=es2018 --format=esm \
  --define:process.env.NODE_ENV=\"production\" \
  --outfile=public/bundle.js
```

### Bundle a Node.js CLI tool

```bash
esbuild src/cli.ts --bundle --platform=node --target=node18 \
  --packages=external --outfile=dist/cli.js
```

### Produce both ESM and CommonJS outputs

```bash
esbuild src/lib.ts --bundle --format=esm --outfile=dist/lib.mjs
esbuild src/lib.ts --bundle --format=cjs --outfile=dist/lib.cjs
```

### Code splitting (shared chunks between entry points)

```bash
esbuild a.js b.js --bundle --splitting --format=esm --outdir=dist
```

> [!NOTE]
> Code splitting (`--splitting`) currently requires `--format=esm`. It extracts code shared between multiple entry points (and dynamic `import()` targets) into separate chunk files to avoid duplication.

### Inject a shim into every file

```bash
# Auto-import a Buffer shim wherever Buffer is used
esbuild app.js --bundle --inject:./buffer-shim.js --outfile=out.js
```

### Bundle CSS

```bash
esbuild src/styles.css --bundle --minify --outfile=dist/styles.css
```

esbuild understands `@import` rules, `url()` references, and CSS modules (`local-css` loader). It can bundle CSS as a first-class entry point, not just CSS imported from JS.

### Analyze what's in your bundle

```bash
esbuild app.js --bundle --analyze --outfile=out.js
```

```text
  out.js  142.7kb
   ├ node_modules/react-dom/index.js  130.2kb  91.2%
   ├ node_modules/react/index.js       6.9kb   4.8%
   └ src/app.js                        5.6kb   3.9%
```

Use `--analyze=verbose` for a deeper per-import breakdown, or `--metafile=meta.json` to write machine-readable build metadata you can inspect with other tools.

---

## 6. The Loader System

A **loader** tells esbuild how to interpret a file based on its extension. Set loaders with `--loader:.ext=name` (build) or `--loader=name` (transform/stdin).

| Loader | Use it for | Behavior |
|---|---|---|
| `js`, `jsx`, `ts`, `tsx` | Source code | Parse and transform JavaScript/TypeScript/JSX |
| `json` | `.json` | Parse to a JS object (tree-shakeable) |
| `css` | Global stylesheets | Bundle CSS |
| `local-css` | CSS Modules | Scope class names locally |
| `global-css` | CSS | Force global (non-scoped) CSS |
| `text` | `.txt`, templates | Import file contents as a UTF-8 string |
| `base64` | Binary | Import as a base64-encoded string |
| `binary` | Binary | Import as a `Uint8Array` |
| `dataurl` | Images, fonts | Inline as a `data:` URL |
| `file` | Assets | Copy to output dir, import resolves to the URL path |
| `copy` | Assets | Copy the file verbatim, import resolves to its path |
| `empty` | Anything | Replace the file's contents with nothing |
| `default` | — | Use esbuild's built-in choice for the extension |

Example — treat `.png` as an inlined data URL and `.svg` as a copied asset:

```bash
esbuild app.js --bundle \
  --loader:.png=dataurl \
  --loader:.svg=file \
  --outfile=out.js
```

> [!TIP]
> Use the `file` loader for large assets (they're copied and referenced by URL) and `dataurl` for small ones (inlined to avoid an extra HTTP request). The `--public-path=` flag sets the base URL prepended to `file`-loader references.

---

## 7. Using esbuild from JavaScript

The JS API gives you everything the CLI does, plus programmatic control. Every option below maps to a CLI flag of the same name (camelCase ⇄ kebab-case).

### 7.1 `build()`

```js
import * as esbuild from 'esbuild'

const result = await esbuild.build({
  entryPoints: ['src/app.ts'],
  bundle: true,
  minify: true,
  sourcemap: true,
  target: ['es2018'],
  outfile: 'dist/app.js',
})

console.log(result.warnings) // []
```

`build()` returns a `BuildResult` with `errors`, `warnings`, and (depending on options) `outputFiles`, `metafile`, and `mangleCache`. It **throws** a `BuildFailure` if the build has errors.

### 7.2 `transform()`

Transform a single in-memory string — no file system access:

```js
import { transform } from 'esbuild'

const result = await transform('let x: number = 1', {
  loader: 'ts',
  minify: true,
})

console.log(result.code) // "let x=1;\n"
```

### 7.3 In-memory output (`write: false`)

Get output as objects instead of writing to disk:

```js
const result = await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  write: false,
})

for (const file of result.outputFiles) {
  console.log(file.path, file.text)
}
```

### 7.4 Build contexts: rebuild, watch, serve

`context()` creates a reusable build you can rebuild incrementally, watch, or serve:

```js
const ctx = await esbuild.context({
  entryPoints: ['app.js'],
  bundle: true,
  outdir: 'www/js',
})

await ctx.watch()                       // rebuild on file changes
const { host, port } = await ctx.serve({ servedir: 'www' })
console.log(`Serving on http://${host}:${port}`)

// Later, when you're done:
await ctx.dispose()
```

| Method | Purpose |
|---|---|
| `ctx.rebuild()` | Rebuild now, reusing cached work |
| `ctx.watch(options?)` | Auto-rebuild on file changes |
| `ctx.serve(options?)` | Start the dev server |
| `ctx.cancel()` | Cancel an in-progress rebuild |
| `ctx.dispose()` | Release the context and stop the background process |

### 7.5 Synchronous variants

`buildSync()` and `transformSync()` block until done. They work only in Node.js (not the browser or Deno) and **cannot use plugins**.

```js
import { transformSync } from 'esbuild'
const { code } = transformSync('1+2', { minify: true })
```

> [!WARNING]
> Prefer the async APIs. The synchronous APIs block the event loop and are noticeably slower for repeated calls. They exist for build scripts and tools that cannot use `async`/`await`.

### 7.6 Plugins

Plugins hook into module resolution and loading. Register them with the `plugins` option:

```js
const envPlugin = {
  name: 'env',
  setup(build) {
    // Intercept imports of "env"
    build.onResolve({ filter: /^env$/ }, args => ({
      path: args.path,
      namespace: 'env-ns',
    }))
    // Provide their contents
    build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
      contents: JSON.stringify(process.env),
      loader: 'json',
    }))
  },
}

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  plugins: [envPlugin],
  outfile: 'out.js',
})
```

| Hook | Fires when | Typical use |
|---|---|---|
| `onResolve` | esbuild needs to resolve an import path | Redirect, externalize, or namespace a path |
| `onLoad` | esbuild needs a module's contents | Generate or transform source on the fly |
| `onStart` | Each build (and rebuild) begins | Setup, validation |
| `onEnd` | Each build finishes | Reporting, post-processing |
| `onDispose` | The context is disposed | Cleanup |

> [!IMPORTANT]
> Plugins require the **asynchronous** API (`build()` or `context()`). They cannot be used with `buildSync()` / `transformSync()`.

### 7.7 Cleaning up (`stop()`)

The JS API keeps a long-lived child process alive for speed. In Node this is cleaned up automatically on exit. In **Deno**, you must call `esbuild.stop()` yourself when finished, or the process will keep running.

---

## 8. Using esbuild in the Browser

The `esbuild-wasm` package runs the engine via WebAssembly. You must call `initialize()` once before any other API call:

```js
import * as esbuild from 'esbuild-wasm'

await esbuild.initialize({
  wasmURL: 'https://unpkg.com/esbuild-wasm@0.28.0/esbuild.wasm',
})

const result = await esbuild.transform('let x: number = 1', { loader: 'ts' })
console.log(result.code)
```

| `initialize()` option | Meaning |
|---|---|
| `wasmURL` | URL of the `esbuild.wasm` file to download |
| `wasmModule` | A pre-compiled `WebAssembly.Module` (alternative to `wasmURL`) |
| `worker` | Run in a Web Worker (default `true`) to keep the UI thread free |

> [!NOTE]
> In the browser, only `build()`, `transform()`, `context()`, `formatMessages()`, and `analyzeMetafile()` are available. The `*Sync` variants throw — WebAssembly has no synchronous child process. `build()` works but cannot write to the file system; use `write: false` and read `outputFiles`.

---

## 9. Troubleshooting

esbuild's most common problems are **installation** issues caused by its platform-specific binary packages. The error messages are intentionally detailed — read them fully.

### Error: `Expected "0.28.0" but got "0.X.Y"`

**Cause:** The installed `esbuild` JavaScript package and its native binary are different versions, usually from a partial or cached install.
**Solution:** Reinstall cleanly:

```bash
rm -rf node_modules package-lock.json
npm install
```

**Prevention:** Pin an exact version and avoid manually editing `node_modules`.

### Error: `The package "@esbuild/<platform>" could not be found, and is needed by esbuild`

**Cause:** esbuild was installed with `--no-optional` or `--omit=optional`, which skips the platform binary package.
**Solution:** Reinstall **without** those flags. esbuild relies on `optionalDependencies` to fetch the correct binary for your OS/CPU.
**Prevention:** Never pass `--no-optional`/`--omit=optional` when installing esbuild.

### Error: `You installed esbuild for another platform than the one you're currently using`

**Cause:** A `node_modules` directory built on one platform was copied to another — classic when copying into a Docker image, or between Windows and WSL.
**Solution:** Don't copy `node_modules` across platforms. Copy only your source and `package.json`, then run `npm ci` on the destination.
**Prevention:** Add `node_modules` to `.dockerignore`; install dependencies inside the target image.

### Error: `dyld: Symbol not found: _SecTrustEvaluateWithError` (macOS)

**Cause:** Your version of macOS is too old for the Go compiler that builds the esbuild binary.
**Solution:** Update macOS, **or** switch to the `esbuild-wasm` package, which runs anywhere:

```bash
npm uninstall esbuild && npm install esbuild-wasm
```

### Error (macOS, Rosetta 2): architecture mismatch between `darwin-x64` and `darwin-arm64`

**Cause:** npm ran under Rosetta 2 (installing the x64 binary) while node runs natively as arm64, or vice versa.
**Solution:** Ensure both npm and node run under the same architecture, then reinstall. Installing node from the official universal installer at <https://nodejs.org/en/download/> avoids this.

### The "esbuild" JavaScript API cannot be bundled

**Cause:** You bundled the `esbuild` package itself into another file, breaking the relative path it uses to find its binary.
**Solution:** Mark `esbuild` as **external** when bundling your own tool:

```bash
esbuild my-tool.js --bundle --platform=node --external:esbuild --outfile=tool.js
```

### Output still contains `import`/`require` statements

**Cause:** You forgot `--bundle`. Without it, esbuild transforms files individually and leaves imports intact.
**Solution:** Add `--bundle`.

### `--watch` stops on its own

**Cause:** esbuild ends watch mode when stdin closes, to avoid orphaned processes.
**Solution:** Keep the parent process alive, or use `--watch=forever` to ignore stdin.

### A modern feature won't compile to my old target

**Cause:** Some syntax cannot be safely lowered to very old environments.
**Solution:** Raise `--target`, or avoid the unsupported feature. Read the error — it names the feature and the conflicting target.

> [!TIP]
> If npm installs are blocked by a firewall but you have a custom registry, set `NPM_CONFIG_REGISTRY`. esbuild's installer (and the Deno loader) honor it when downloading the binary package.

---

## 10. Frequently Asked Questions

**Q: Does esbuild type-check my TypeScript?**
No. esbuild only strips types and transforms syntax — it never reports type errors. Run `tsc --noEmit` separately for type checking.

**Q: Why is the output not bundled?**
`--bundle` is opt-in. Add it. Without it, esbuild processes each entry point independently.

**Q: How do I get a single output file?**
Use `--outfile=out.js`. Use `--outdir=dir` only for multiple entry points or code splitting.

**Q: Can I use plugins from the command line?**
No. Plugins are a JavaScript/Go API feature. Use the `build()`/`context()` API (or the Go `pkg/cli` `RunWithPlugins`) to load plugins.

**Q: Does esbuild support code splitting?**
Yes, with `--splitting` — currently only when `--format=esm`.

**Q: What's the difference between `esbuild` and `esbuild-wasm`?**
`esbuild` runs a native binary (fast). `esbuild-wasm` runs WebAssembly (portable, works in browsers, but can be several times slower). Use `esbuild-wasm` only when the native binary won't run.

**Q: Can I run esbuild without installing it globally?**
Yes — `npx esbuild …`, a `package.json` script, or the programmatic API all use your locally pinned version.

**Q: How do I substitute an environment variable at build time?**
Use `--define`, remembering that values are raw expressions: `--define:process.env.NODE_ENV=\"production\"`.

**Q: Does esbuild minify CSS?**
Yes. Pass a `.css` entry point with `--minify` (and `--bundle` to follow `@import`).

**Q: How do I keep `node_modules` out of my bundle?**
Use `--packages=external` (all packages) or `--external:<name>` (specific ones).

**Q: Why does my Deno script never exit after using esbuild?**
Deno can't auto-clean the child process. Call `await esbuild.stop()` when you're done.

**Q: Can esbuild target both ESM and CommonJS?**
Yes — run it twice with `--format=esm` and `--format=cjs`, choosing different output extensions (e.g. `.mjs`/`.cjs` or `--out-extension:.js=.mjs`).

**Q: Is esbuild production-ready even though it's `0.x`?**
It is widely used in production, but being pre-1.0 means minor releases can introduce breaking changes. Pin an exact version and read the changelog before upgrading.

**Q: How do I see what's taking up space in my bundle?**
Use `--analyze` (or `--analyze=verbose`) for a printed report, or `--metafile=meta.json` for machine-readable data.

---

## 11. CLI Quick Reference

### Simple options

| Flag | Description |
|---|---|
| `--bundle` | Inline all dependencies into the output |
| `--define:K=V` | Replace identifier `K` with expression `V` while parsing |
| `--external:M` | Exclude module `M` from the bundle (supports `*` wildcards) |
| `--format=iife\|cjs\|esm` | Output module format |
| `--loader:.ext=L` | Use loader `L` for files of extension `.ext` |
| `--minify` | Minify output (whitespace + identifiers + syntax) |
| `--outdir=dir` | Output directory (for multiple entry points) |
| `--outfile=file` | Output file (for a single entry point) |
| `--packages=external` | Keep all imported packages external |
| `--platform=browser\|node\|neutral` | Resolution/defaults platform (default `browser`) |
| `--serve=host:port` | Start a local HTTP dev server |
| `--sourcemap` | Emit a source map |
| `--splitting` | Enable code splitting (ESM only) |
| `--target=...` | Environment target (e.g. `es2017`, `chrome58`, `node18`) |
| `--watch` | Rebuild on file system changes |

### Frequently used advanced options

| Flag | Description |
|---|---|
| `--alias:from=to` | Replace one package name with another |
| `--analyze` / `--analyze=verbose` | Print a bundle composition report |
| `--banner:js=...` / `--footer:js=...` | Prepend/append text to output of a given type (`js`/`css`) |
| `--charset=utf8` | Don't escape UTF-8 code points in output |
| `--conditions=...` | `package.json` export conditions to honor |
| `--drop:console` / `--drop:debugger` | Remove `console.*` calls / `debugger` statements |
| `--inject:F` | Auto-import file `F` into every input file |
| `--jsx=automatic\|transform\|preserve` | JSX transform mode |
| `--keep-names` | Preserve original `.name` on functions and classes |
| `--legal-comments=...` | Where to place legal comments (`none`/`inline`/`eof`/`linked`/`external`) |
| `--log-level=...` | `verbose`/`debug`/`info`/`warning`/`error`/`silent` (default `info`) |
| `--mangle-props=regex` | Rename properties matching a regex |
| `--metafile=file` | Write build metadata JSON |
| `--out-extension:.js=.mjs` | Customize output file extension |
| `--public-path=...` | Base URL for the `file` loader |
| `--pure:N` | Mark `N` as a side-effect-free function for tree shaking |
| `--tree-shaking=true\|false` | Force tree shaking on or off |
| `--tsconfig=file` | Use a specific `tsconfig.json` |
| `--watch=forever` | Watch even after stdin closes |

> [!NOTE]
> Flags that can be specified **once** use `=` (e.g. `--outfile=x`). Flags that can be specified **multiple times** use `:` (e.g. `--define:K=V`, `--external:M`, `--loader:.ext=L`). esbuild will suggest the correct form if you mix them up.

### Environment variables

| Variable | Effect |
|---|---|
| `NO_COLOR` | Disable colored terminal output |
| `NODE_PATH` | Extra directories to resolve modules from (Node convention) |
| `ESBUILD_BINARY_PATH` | Override the path to the esbuild binary the JS API runs |
| `ESBUILD_WORKER_THREADS` | Set to `0` to disable the worker thread used by the sync APIs |
| `ESBUILD_MAX_BUFFER` | Max bytes of output the sync API will buffer (default 16 MB) |

---

_Generated June 2026 from evanw/esbuild at 6a794df. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
