esbuild is the bundler that made me realize I'd been wasting years waiting for webpack builds. Evan Wallace wrote this in Go, and holy shit the difference is night and day. While webpack spends forever parsing its own JavaScript runtime, esbuild is already done. Those "100x faster" claims? Not bullshit. Saw a 45-second webpack build drop to 0.8 seconds on esbuild 0.19.2.
Why esbuild is Fast as Hell
The speed isn't just impressive - it's life-changing. When you're used to waiting 40+ seconds for a webpack build, getting it down to under 1 second changes how you work. You actually start enjoying development again instead of dreading every code change. Here's why it's so damn fast:
Native Go Implementation: Most JS build tools run in Node.js and get murdered by garbage collection. esbuild is compiled Go - no JavaScript runtime overhead. It's like switching from an interpreted language to C. Your CPU can actually do the work instead of cleaning up memory.
Parallelization: esbuild uses all your CPU cores by default. While webpack processes files one by one like it's 1995, esbuild parses multiple files simultaneously. On a 8-core machine, that's a massive difference.
Zero npm Dependencies: Check the esbuild package.json - zero dependencies. No sprawling dependency tree that takes longer to install than your actual code. No random security vulnerabilities from packages you've never heard of.
What esbuild Actually Handles
esbuild covers most of what you need without the plugin hell:
- JavaScript: ES2015+ support with automatic syntax targeting to whatever browsers you specify
- TypeScript: Strips types, doesn't check them. Your build succeeds while your types are fucked, and you only find out when production crashes with "Cannot read property 'undefined' of undefined" at 2am. Always run
tsc --noEmit
in CI or pay the price. - JSX: React JSX transformation works great, configurable for different JSX factories
- CSS: Basic CSS bundling and minification. CSS modules work but PostCSS integration requires plugins.
The bundler handles both ESM and CommonJS and converts between them automatically. You can import CommonJS modules from ES modules without thinking about it, which is nice when half your dependencies are still using require()
.
The Good Stuff That Actually Works
Tree Shaking: esbuild's dead code elimination is aggressive and works well. It'll remove unused imports and exports across JavaScript and CSS. Just don't expect it to be as smart as Rollup's tree shaking for complex cases.
Source Maps: Source map generation works reliably for debugging. The maps are accurate and load fast. You can configure detail levels, though in practice you usually just want them on for dev and off for production.
Code Splitting: Automatic code splitting creates separate bundles for shared dependencies. Works well for basic cases, but you'll miss webpack's fine-grained control over chunk creation if you need complex splitting strategies.
Target Configuration: Specify browser targets and esbuild transforms syntax accordingly. No Babel configuration hell - just set target: ['chrome80', 'firefox75']
and it works. Though you still need to handle polyfills separately with something like core-js.
How to Actually Use esbuild
esbuild gives you three ways to run it, and honestly you'll probably stick to one:
- Command Line Interface:
esbuild app.js --bundle --outfile=out.js
- perfect for simple builds - JavaScript API: Programmatic access when you need to integrate with build scripts or custom tooling
- Go API: If you're building Go tools, though most of us aren't
The JavaScript API handles both build operations (bundle entire apps) and transform operations (process individual files). The API is clean and doesn't require learning 20 different configuration options like webpack.
The Built-in Dev Server (It's... Okay)
esbuild's development server exists but it's pretty basic. Don't expect webpack-dev-server features:
- Live Reload: Browser refresh when files change, but no hot module replacement like webpack's HMR
- Watch Mode: Rebuilds on file changes fast as hell, which is the main benefit
- Limited Middleware: Basic static file serving, but if you need proxy support or complex routing, you're stuck
Honestly, most people use Vite which uses esbuild under the hood but provides a better dev server experience. Or just use esbuild's watch mode with your own static server.
Real-World Adoption (People Actually Use This)
Last I checked, esbuild gets like 57 million weekly downloads on npm. That's not inflated bullshit - people are actually using it. The GitHub repo has almost 40k stars, though Evan Wallace mostly maintains this himself, which is both impressive and slightly terrifying for a production dependency.
The real validation is that other tools use esbuild under the hood:
- Vite uses esbuild for dependency pre-bundling and TypeScript compilation
- SvelteKit uses esbuild for production builds
- Astro leverages esbuild for fast builds
- Biome (formerly Rome) took inspiration from esbuild's Go-based approach
Other build tools started using esbuild under the hood because the speed gains are impossible to ignore. The ecosystem adoption proves esbuild works at scale, not just in demos.
Here's what bites you: esbuild doesn't type check. Period. I shipped a broken interface change once because the build passed but TypeScript was screaming about missing properties. Production got a nice "TypeError: Cannot read property 'map' of undefined" instead of my feature. Run tsc --noEmit
in your CI pipeline or learn the hard way like I did. The plugin ecosystem is also tiny - if you need webpack's army of loaders, you're fucked.
The development server is pretty basic too - no hot module replacement like webpack-dev-server, just page reloads. Most people end up using Vite which gives you esbuild's speed with better development experience, or they stick with esbuild's watch mode and a separate static server.