esbuild: Fast JavaScript and TypeScript Bundling
In this tutorial, you'll learn esbuild including bundling, Transpilation, Minification, plugins, and integrating with build scripts for extremely fast JavaScript and TypeScript builds.
Why esbuild Matters
Build speed has become a bottleneck in modern web development. Webpack, Rollup, and Parcel process each file through transformation pipelines that become slower as projects grow. esbuild rewrites the bundling process from the ground up in Go, achieving 10-100x speed improvements over JavaScript-based bundlers. This speed makes it ideal for development builds, CI pipelines, and as the foundation for tools like Vite.
By the end of this guide, you will bundle JavaScript and TypeScript with esbuild, configure plugins, and integrate it into your build pipeline.
What is esbuild?
esbuild is an extremely fast JavaScript bundler and minifier written in Go. It handles bundling, Tree Shaking, TypeScript stripping, JSX transformation, CSS bundling, and Minification.
flowchart LR A[Source Files] --> B[esbuild] B --> C[Lexing] C --> D[Parsing] D --> E[AST] E --> F[Transformation] F --> G[Code Generation] G --> H[Bundled Output] B --> I[Minification] B --> J[Source Maps] B --> K[Tree Shaking]
Installation
# npm
npm install --save-dev esbuild
# Or use the standalone binary via curl
curl -fsSL https://esbuild.github.io/dl/latest | sh
Verify
npx esbuild --version
# 0.24.0
Basic Usage
Command Line
# Bundle a single file
npx esbuild src/index.js --bundle --outfile=dist/bundle.js
# Bundle with minification
npx esbuild src/index.js --bundle --minify --outfile=dist/bundle.js
# Watch mode
npx esbuild src/index.js --bundle --watch --outfile=dist/bundle.js
# TypeScript
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js
Expected Output
$ npx esbuild src/index.js --bundle --outfile=dist/bundle.js
dist/bundle.js 12.5kb
⚡ Done in 8ms
API Usage
esbuild's JavaScript API is more flexible than the CLI.
const esbuild = require('esbuild')
// Simple build
esbuild.buildSync({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
})
Async Build
async function build() {
const result = await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
minify: true,
sourcemap: true,
target: ['es2020'],
})
console.log('Build complete:', result)
}
build()
Watch Mode
async function watch() {
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
})
await ctx.watch()
console.log('Watching...')
}
watch()
TypeScript Support
esbuild handles TypeScript without configuration. It strips types and compiles to JavaScript.
// src/index.ts
interface User {
name: string
age: number
}
const user: User = { name: 'Alice', age: 30 }
console.log(user.name)
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js
Expected Output
// dist/bundle.js
const user = { name: "Alice", age: 30 };
console.log(user.name);
Note: esbuild strips TypeScript types but does not perform Type Checking. Use tsc --noEmit separately for Type Checking.
TSConfig Paths
esbuild.buildSync({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
alias: {
'@': './src',
},
})
JSX and React
const esbuild = require('esbuild')
esbuild.buildSync({
entryPoints: ['src/app.jsx'],
bundle: true,
outfile: 'dist/app.js',
loader: { '.jsx': 'jsx' },
jsxFactory: 'React.createElement',
jsxFragment: 'React.Fragment',
})
For React 17+ with the new JSX transform:
esbuild.buildSync({
entryPoints: ['src/app.jsx'],
bundle: true,
outfile: 'dist/app.js',
jsx: 'automatic',
jsxImportSource: 'react',
})
Minification
esbuild.buildSync({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
outfile: 'dist/bundle.js',
})
Comparison
| Build | Size | Time |
|---|---|---|
| Without minify | 45 KB | 12 ms |
| With minify | 18 KB | 18 ms |
| With minify + whitespace only | 22 KB | 14 ms |
CSS Bundling
esbuild also bundles CSS:
esbuild.buildSync({
entryPoints: ['src/styles.css'],
bundle: true,
outfile: 'dist/styles.css',
loader: { '.png': 'dataurl' },
})
Plugins
esbuild has a plugin API for custom transforms and loaders.
Simple Plugin
const envPlugin = {
name: 'env',
setup(build) {
// Intercept import paths called "env"
build.onResolve({ filter: /^env$/ }, args => {
return { path: args.path, namespace: 'env' }
})
// Load env variables
build.onLoad({ filter: /.*/, namespace: 'env' }, () => {
return {
contents: JSON.stringify(process.env),
loader: 'json',
}
})
},
}
esbuild.buildSync({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [envPlugin],
})
External Plugin
const externalPlugin = {
name: 'external-react',
setup(build) {
build.onResolve({ filter: /^react$/ }, args => {
return { path: args.path, external: true }
})
},
}
Integration with Build Scripts
Node.js Build Script
#!/usr/bin/env node
// scripts/build.js
const esbuild = require('esbuild')
const glob = require('glob')
async function build() {
const entryPoints = glob.sync('src/**/*.ts').filter(f => !f.endsWith('.test.ts'))
await esbuild.build({
entryPoints,
bundle: false,
outdir: 'dist',
format: 'esm',
target: 'node18',
platform: 'node',
sourcemap: true,
})
console.log(`Built ${entryPoints.length} files`)
}
build().catch(() => process.exit(1))
Package.json Scripts
{
"scripts": {
"build": "node scripts/build.js",
"build:prod": "node scripts/build.js --minify"
}
}
Performance Benchmark
# esbuild
time npx esbuild src/index.js --bundle --minify --outfile=dist/bundle.js
# real 0m0.089s
# Webpack (for comparison)
time npx webpack --mode production
# real 0m2.450s
Common Errors
| Problem | Cause | Fix |
|---|---|---|
No loader configured for ".vue" files |
Vue files not handled | esbuild does not support Vue SFCs natively; use a plugin or Vite |
Cannot use import statement outside a module |
Not bundling ES modules | Add --bundle flag |
Type error: Property 'xyz' does not exist |
esbuild strips types but does not check them | Run tsc --noEmit separately |
Build failed with 1 error: Expected ";" |
Syntax error in source | Check the line indicated in the error |
Output file is empty |
Entry file exports nothing | Ensure the entry file has side effects or a default export |
Practice Questions
1. What language is esbuild written in?
Go.
2. How do you enable Minification in esbuild?
Use the --minify flag or minify: true in the API.
3. Does esbuild perform TypeScript Type Checking?
No. It strips types but does not check them.
4. What is the --bundle flag for?
It tells esbuild to resolve and bundle all imported dependencies into a single output file.
5. How do you watch for changes with esbuild?
Use --watch in the CLI or await ctx.watch() in the API.
Challenge
Create a Node.js script that uses esbuild's API to build a TypeScript library. The build should output both ESM and CommonJS formats, generate source maps, and minify the production build. Include an onResolve plugin that aliases a module path.
Real-World Task
Replace Webpack or another bundler with esbuild in a small to medium-sized project. Configure the build to handle JavaScript, TypeScript, CSS, and JSON files. Set up watch mode for development and production Minification. Measure the build time improvement. Run tsc --noEmit as a separate step for Type Checking.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro