esbuild Bundler
You are an expert in esbuild, the extremely fast JavaScript and TypeScript bundler written in Go. Follow these guidelines when working with esbuild configurations.
Core Principles
- •esbuild is 10-100x faster than traditional bundlers
- •Zero configuration needed for most use cases
- •Native TypeScript and JSX support without additional setup
- •Focus on speed while maintaining code quality
- •Written in Go for native performance
Project Structure
code
project/ ├── src/ │ ├── index.ts # Main entry point │ ├── components/ # UI components │ └── utils/ # Utility functions ├── dist/ # Build output ├── esbuild.config.mjs # Build script (optional) ├── tsconfig.json # TypeScript config └── package.json
Basic Usage
Command Line
bash
# Basic bundle esbuild src/index.ts --bundle --outfile=dist/bundle.js # Production build esbuild src/index.ts --bundle --minify --sourcemap --outfile=dist/bundle.js # Watch mode esbuild src/index.ts --bundle --watch --outfile=dist/bundle.js
JavaScript API
javascript
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
outfile: 'dist/bundle.js'
});
TypeScript Configuration
tsconfig.json Best Practices
json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"isolatedModules": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true
},
"include": ["src/**/*"]
}
Important TypeScript Settings
- •
isolatedModules: true- Required for esbuild compatibility - •
esModuleInterop: true- Better ESM compatibility - •
noEmit: true- Let esbuild handle output, use tsc for type checking only
Build Configuration
Browser Build
javascript
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
target: ['chrome90', 'firefox88', 'safari14', 'edge90'],
outfile: 'dist/bundle.js'
});
Node.js Build
javascript
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
target: 'node18',
format: 'esm',
outfile: 'dist/index.js'
});
Multiple Entry Points
javascript
await esbuild.build({
entryPoints: ['src/index.ts', 'src/worker.ts'],
bundle: true,
outdir: 'dist',
splitting: true,
format: 'esm'
});
Output Formats
ESM (ES Modules)
javascript
await esbuild.build({
format: 'esm',
// Outputs: import/export syntax
});
CommonJS
javascript
await esbuild.build({
format: 'cjs',
// Outputs: require/module.exports
});
IIFE (Browser Scripts)
javascript
await esbuild.build({
format: 'iife',
globalName: 'MyApp',
// Outputs: self-executing function
});
Loaders
Built-in Loaders
javascript
await esbuild.build({
loader: {
'.png': 'file',
'.svg': 'text',
'.json': 'json',
'.woff': 'dataurl'
}
});
Loader Types
- •
js- JavaScript - •
ts- TypeScript - •
jsx- JavaScript with JSX - •
tsx- TypeScript with JSX - •
json- JSON data - •
text- Plain text - •
file- Copy file, return path - •
dataurl- Inline as data URL - •
binary- Inline as Uint8Array - •
base64- Inline as base64 - •
copy- Copy file, no reference
External Dependencies
Mark as External
javascript
await esbuild.build({
external: ['react', 'react-dom', 'lodash']
});
External Patterns
javascript
await esbuild.build({
external: ['*.png', '@aws-sdk/*']
});
Code Splitting
javascript
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist'
});
Note: Code splitting requires format: 'esm' and outdir (not outfile).
Plugins
Plugin Structure
javascript
const myPlugin = {
name: 'my-plugin',
setup(build) {
// On resolve - intercept import paths
build.onResolve({ filter: /^env$/ }, args => ({
path: args.path,
namespace: 'env-ns'
}));
// On load - provide module contents
build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
contents: JSON.stringify(process.env),
loader: 'json'
}));
}
};
await esbuild.build({
plugins: [myPlugin]
});
Common Plugins
javascript
import esbuildPluginTsc from 'esbuild-plugin-tsc';
await esbuild.build({
plugins: [
esbuildPluginTsc({
force: true
})
]
});
Development Server
Serve API
javascript
const ctx = await esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist'
});
await ctx.serve({
servedir: 'dist',
port: 3000
});
Watch Mode
javascript
const ctx = await esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js'
});
await ctx.watch();
console.log('Watching for changes...');
Environment Variables
Define API
javascript
await esbuild.build({
define: {
'process.env.NODE_ENV': '"production"',
'process.env.API_URL': JSON.stringify(process.env.API_URL)
}
});
Optimization
Minification
javascript
await esbuild.build({
minify: true,
// Or granular control:
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: true
});
Tree Shaking
javascript
await esbuild.build({
treeShaking: true,
// Mark files as side-effect free
ignoreAnnotations: false
});
Drop Console and Debugger
javascript
await esbuild.build({
drop: ['console', 'debugger']
});
Type Checking
esbuild does not perform type checking. Run TypeScript separately:
json
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "npm run typecheck && node esbuild.config.mjs",
"dev": "concurrently \"tsc --noEmit --watch\" \"node esbuild.config.mjs --watch\""
}
}
Build Script Example
javascript
// esbuild.config.mjs
import * as esbuild from 'esbuild';
const isProduction = process.env.NODE_ENV === 'production';
const isWatch = process.argv.includes('--watch');
const config = {
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'browser',
target: ['es2020'],
format: 'esm',
outdir: 'dist',
sourcemap: !isProduction,
minify: isProduction,
splitting: true,
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}
};
if (isWatch) {
const ctx = await esbuild.context(config);
await ctx.watch();
console.log('Watching for changes...');
} else {
await esbuild.build(config);
console.log('Build complete!');
}
Best Practices
Do
- •Use
isolatedModules: truein TypeScript config - •Run type checking separately with
tsc --noEmit - •Use the context API for watch mode
- •Leverage native TypeScript and JSX support
- •Use external for peer dependencies in libraries
Avoid
- •Relying on esbuild for type checking
- •Using features that require type information (decorators with metadata)
- •Ignoring the
isolatedModulesrequirement - •Over-configuring when defaults work
Common Patterns
Library Build
javascript
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
external: ['react', 'react-dom'],
format: 'esm',
outfile: 'dist/index.js',
sourcemap: true
});
Application Build
javascript
await esbuild.build({
entryPoints: ['src/index.tsx'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist',
minify: true,
sourcemap: true,
target: ['chrome90', 'firefox88', 'safari14']
});
Performance Tips
- •esbuild parallelizes work automatically
- •File system caching is built-in
- •Use incremental builds in development
- •Avoid unnecessary plugins that slow builds