esbuild
esbuild is a JavaScript and TypeScript bundler and minifier written in Go by Evan Wallace. Its headline feature is speed: it routinely bundles large projects 10–100 times faster than older JavaScript-based tools. That speed made it the engine inside many other tools — Vite uses it to pre-bundle dependencies and transform TypeScript in development, and tools like tsup, Bun's early builds, and countless CLIs rely on it.
You can use esbuild directly for libraries, Node services, serverless functions, and simple web apps, or indirectly through higher-level tools. Its design philosophy is to be fast and simple, and it intentionally leaves some features — notably type checking — to other tools.
TL;DR
- esbuild bundles, transforms, and minifies JS, TS, JSX, TSX, CSS, and JSON very quickly.
- It's fast because it's written in Go, runs in parallel, and makes few passes over the AST.
- It strips TypeScript types but doesn't type-check — run
tsc --noEmitseparately. - Use it via the CLI or the JavaScript/Go API; extend it with plugins for custom resolution and loading.
- Great for libraries, Node/serverless bundles, and dev tooling; many apps use it through Vite.
- It doesn't aim to replace every webpack feature (e.g. advanced chunking controls or HMR runtime).
Quick Example
Bundle a TypeScript React app for the browser, with minification and source maps:
Bundle a Node.js Lambda handler into a single file, leaving the AWS SDK external:
metafile: true produces a JSON report of what ended up in the bundle — feed it to the esbuild bundle analyzer to find heavy dependencies.
Core Concepts
Why esbuild Is Fast
- Native code — compiled Go, rather than JavaScript running on a JIT.
- Parallelism — parsing, printing, and source map generation use all CPU cores.
- Few passes — parsing, transforming, and minifying happen in a small number of passes over the syntax tree with memory-friendly data structures.
- No generic plugin pipeline by default — built-in loaders handle common file types without calling back into JavaScript.
Build vs Transform
build— resolves imports from entry points, bundles modules together, and writes output files.transform— converts a single string of code (TS → JS, JSX → JS, minify) without touching the file system. Tools use this for fast per-file compilation.
Loaders
Loaders tell esbuild how to interpret a file type: js, ts, jsx, tsx, css, json, text, base64, dataurl, file, copy, and empty. For example, --loader:.png=file copies images to the output directory and returns their URL.
Platforms, Formats, and Targets
esbuild lowers modern syntax to older targets where it can, but it doesn't add polyfills for missing runtime APIs.
Plugins
Plugins hook into resolution (onResolve) and loading (onLoad). Common uses: aliasing paths, loading files from virtual modules, compiling Svelte or Vue files, and integrating environment-specific imports.
Watch and Serve
context() returns a build context with watch() for incremental rebuilds and serve() for a simple development server. Incremental rebuilds reuse parsed files and are often a few milliseconds.
What esbuild Leaves Out
- Type checking — use
tsc --noEmitor your editor. - Some TypeScript features that need type information, such as
emitDecoratorMetadata. - Fine-grained chunking control — code splitting works for ESM output but with fewer knobs than Rollup or webpack.
- HMR runtime — frameworks and Vite provide that on top.
Best Practices
Pair It With a Type Checker
Run tsc --noEmit in CI and your editor. esbuild compiling successfully doesn't mean your types are correct.
Set target Explicitly
Match your supported browsers or Node version so esbuild lowers exactly what's needed and no more.
Mark Runtime-Provided Packages External
For Node and serverless bundles, externalize packages the runtime already provides or native modules that can't be bundled.
Analyze Bundles With the Metafile
Use metafile output and the analyzer to catch accidental imports of large libraries.
Use It Through Higher-Level Tools for Apps
For full applications with routing, HMR, and asset pipelines, Vite gives you esbuild's speed with a complete developer experience.
Common Mistakes
Expecting Type Errors to Fail the Build
esbuild strips types without checking them. A build can succeed with serious type errors unless tsc runs separately.
Bundling Native Node Modules
Packages with .node binaries can't be bundled. Mark them external and ship them in node_modules.
Assuming Polyfills Are Added
Lowering syntax doesn't add missing APIs like Array.prototype.at for old browsers. Add polyfills explicitly if you target old environments.
Relying on isolatedModules-Incompatible Code
esbuild compiles each file independently. Re-exporting types without export type or using const enum across files can break. Enable isolatedModules in tsconfig.json to catch this.
Comparison
FAQ
What is esbuild used for?
esbuild bundles and minifies JavaScript and TypeScript for browsers and Node.js. It's used directly for libraries, CLIs, and serverless functions, and indirectly inside tools like Vite.
Why is esbuild so fast?
It's compiled Go code that uses parallelism across CPU cores and processes source in very few passes, avoiding much of the overhead of JavaScript-based bundlers.
Does esbuild type-check TypeScript?
No. It removes type annotations and compiles to JavaScript. Run the TypeScript compiler with --noEmit to check types.
Should I use esbuild or Vite?
For a web application, use Vite — it uses esbuild internally and adds a dev server, HMR, and optimized production builds. Use esbuild directly for libraries, Node services, or custom build scripts where you want minimal tooling.
Related Topics
- Vite — The dev server built on esbuild's speed
- webpack — The configurable bundler esbuild is often compared with
- Build Tools & Bundlers — How bundlers fit into a toolchain
- TypeScript — Type checking esbuild leaves to
tsc - Web Performance — Why bundle size and minification matter