Bundling
esbuild
Extremely fast JavaScript bundler
Intermediate
bundling esbuild go performance
Definition
esbuild is an extremely fast JavaScript bundler and minifier written in Go. It handles bundling 10-100x faster than JavaScript-based alternatives like Webpack and Rollup, making it ideal for development workflows and CI/CD pipelines where speed matters.
Performance
Build Speed Comparison
Three.js (10k modules):
Webpack: 45 seconds
Rollup: 32 seconds
Parcel 2: 18 seconds
esbuild: 0.37 seconds ← 100x faster
Large codebase (100k+ modules):
esbuild: 2-5 seconds
Others: 1-5 minutes
Why So Fast?
1. Written in Go (compiled, parallel)
2. Minimal AST transformations
3. No caching overhead (fast enough without)
4. Parallelized parsing and code generation
5. Single-pass tree shaking
Basic Usage
CLI
# Bundle for browser
esbuild app.js --bundle --outfile=out.js
# Watch mode
esbuild app.js --bundle --outfile=out.js --watch
# Development server
esbuild app.js --bundle --outfile=out.js --servedir=.
# Production build
esbuild app.js --bundle --minify --outfile=out.js
JavaScript API
const esbuild = require('esbuild');
// Simple build
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
});
// Multiple outputs
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist',
});
Configuration
Build Options
await esbuild.build({
entryPoints: ['src/index.js'],
// Output
outfile: 'dist/bundle.js',
format: 'esm', // or 'cjs', 'iife'
platform: 'browser', // or 'node'
target: 'es2020',
// Bundling
bundle: true,
splitting: true, // Code splitting (ESM only)
minify: true,
sourcemap: true,
// Optimization
treeShaking: true,
splitting: true,
metafile: true, // Generate build metadata
// Advanced
define: {
'process.env.NODE_ENV': '"production"'
},
loader: {
'.png': 'file',
'.svg': 'dataurl',
'.css': 'css'
},
// External packages
external: ['react', 'react-dom']
});
Features
TypeScript Support
// No plugin needed!
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
// TypeScript compiled automatically
});
// JSX also works
await esbuild.build({
entryPoints: ['src/app.tsx'],
jsx: 'automatic', // or 'transform'
jsxImportSource: 'react'
});
CSS Handling
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
// CSS is bundled automatically
// Import CSS in JS:
// import './styles.css';
});
// CSS as separate file
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [
{
name: 'css',
setup(build) {
build.onResolve({ filter: /\.css$/ }, args => ({
path: args.path,
namespace: 'css'
}));
}
}
]
});
Loader Configuration
await esbuild.build({
loader: {
'.png': 'file', // Copy to output
'.svg': 'dataurl', // Inline as data URL
'.txt': 'text', // Import as string
'.json': 'json', // Import as object
'.css': 'css' // Bundle CSS
},
assetNames: 'assets/[name]-[hash]'
});
Plugins
Using Plugins
const { sassPlugin } = require('esbuild-sass-plugin');
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [
sassPlugin()
]
});
Custom Plugin
const envPlugin = {
name: 'env',
setup(build) {
// Intercept import called "env"
build.onResolve({ filter: /^env$/ }, args => ({
path: args.path,
namespace: 'env-ns'
}));
// Load path with "env-ns" namespace
build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
contents: JSON.stringify(process.env),
loader: 'json'
}));
}
};
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [envPlugin]
});
Development Workflow
Watch Mode
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
});
await ctx.watch();
// Later
await ctx.dispose();
Dev Server
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js'
});
await ctx.serve({
servedir: '.',
port: 8000
});
console.log('Serving on http://localhost:8000');
Live Reload
const livereload = require('livereload');
const connect = require('connect');
const serveStatic = require('serve-static');
const server = livereload.createServer();
server.watch(__dirname + '/dist');
connect()
.use(serveStatic('dist'))
.listen(3000);
Use Cases
Vite’s Dev Server
// Vite uses esbuild for dev builds
// - Lightning fast HMR
// - On-demand compilation
// - ES modules in dev
Library Development
// Build library for npm
await esbuild.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
bundle: true,
splitting: true,
format: 'esm',
external: ['react']
});
CI/CD
// Fast builds in CI
const start = Date.now();
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
outfile: 'dist/bundle.js'
});
console.log(`Built in ${Date.now() - start}ms`);
// Output: Built in 250ms
Limitations
Not 100% Webpack-Compatible
// No built-in support for:
// - Hot Module Replacement (use Vite)
// - CSS Modules (use plugin)
// - Some webpack-specific features
// Use for:
// - Development builds (fast)
// - Production builds (fast minification)
// - Libraries (clean output)
Type Checking
// esbuild transpiles TypeScript but doesn't type check
// Run tsc separately for type checking:
// package.json
{
"scripts": {
"build": "tsc --noEmit && esbuild src/index.ts --bundle --outfile=dist/bundle.js"
}
}
Best Practices
1. Use for Development
// esbuild.config.js
const isDev = process.env.NODE_ENV === 'development';
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
minify: !isDev,
sourcemap: isDev,
watch: isDev
});
2. Combine with TypeScript
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "npm run typecheck && node build.js"
}
}
3. External Dependencies
await esbuild.build({
external: [
// Don't bundle large dependencies
'react',
'react-dom',
'lodash',
// Use peer dependencies
'vue'
]
});
Key Takeaway
esbuild provides 10-100x faster builds than JavaScript bundlers by leveraging Go’s performance. Use it for development builds, CI/CD, and library bundling. While it lacks some Webpack features, its speed makes it ideal for Vite’s dev server and rapid development workflows. Pair with TypeScript compiler for type checking.