|$ curl https://forge-ai.dev/api/markdown?path=docs/build-tools/esbuild
$cat docs/esbuild-&-swc-—-fast-bundlers.md
updated Recently·30 min read·published

esbuild & SWC — Fast Bundlers

Build ToolsPerformanceIntermediate
Introduction

Traditional JavaScript-based build tools are reaching their performance limits. esbuild (Go-based) and SWC (Rust-based) represent a new generation of bundlers and compilers that are 10-100x faster than their predecessors, leveraging systems-level languages for parallel processing and efficient memory management.

These tools are not just faster — they fundamentally change how build pipelines are architected. Modern frameworks like Vite, Next.js, and Turbopack use esbuild and SWC under the hood, making them invisible but essential infrastructure.

esbuild — Go-Based Bundler

Created by Evan Wallace (Figma), esbuild is a JavaScript bundler and minifier written in Go. It achieves its speed through parallelism (using goroutines), efficient memory usage, and a from-scratch implementation that avoids the overhead of existing AST representations.

terminal
Bash
1# Install esbuild
2npm install --save-dev esbuild
3
4# Basic usage — bundle a file
5npx esbuild src/index.js --bundle --outfile=dist/bundle.js
6
7# Bundle with JSX and minification
8npx esbuild src/app.tsx \
9 --bundle \
10 --outfile=dist/app.js \
11 --loader:.tsx=tsx \
12 --minify \
13 --sourcemap \
14 --target=es2020
15
16# Development mode (no minify, with sourcemaps)
17npx esbuild src/app.tsx --bundle --outfile=dist/app.js --sourcemap
BenchmarkesbuildWebpack 5Rollup 4Parcel 2
Bundle (10x React)0.36s15.9s8.2s7.8s
Minify (three.js)0.24s4.2s
TS -> JS (1000 files)0.48s
🔥

pro tip

esbuild's speed comes from three design decisions: it is written in Go (compiled to native code, no JIT warmup), it uses parallelism aggressively (all cores), and it implements its own JS/TS parser from scratch (no dependency on Babel or TypeScript compiler).
esbuild-api.mjs
JavaScript
1// esbuild JavaScript API
2const esbuild = require("esbuild");
3
4// Build programmatically
5await esbuild.build({
6 entryPoints: ["src/index.tsx"],
7 outfile: "dist/bundle.js",
8 bundle: true,
9 minify: true,
10 sourcemap: true,
11 target: "es2020",
12 loader: {
13 ".tsx": "tsx",
14 ".svg": "dataurl",
15 ".png": "file",
16 },
17 define: {
18 "process.env.NODE_ENV": '"production"',
19 },
20 plugins: [/* ... */],
21});
22
23// Watch mode
24const ctx = await esbuild.context({
25 entryPoints: ["src/index.tsx"],
26 outfile: "dist/bundle.js",
27 bundle: true,
28});
29await ctx.watch();
30console.log("Watching for changes...");
31
32// Serve mode (dev server)
33await ctx.serve({
34 port: 3000,
35 servedir: "dist",
36});
37
esbuild Plugin API

esbuild has a well-designed plugin API based on hooks that intercept the build lifecycle. While smaller than Webpack's ecosystem, the API is powerful and composable.

esbuild-plugin.js
JavaScript
1const esbuild = require("esbuild");
2
3// Custom plugin — strip console.log in production
4const stripConsolePlugin = {
5 name: "strip-console",
6 setup(build) {
7 build.onLoad({ filter: /\.(js|ts)x?$/ }, (args) => {
8 // Transform on load
9 });
10
11 build.onEnd((result) => {
12 console.log(`Build finished with ${result.errors.length} errors`);
13 });
14 },
15};
16
17// Plugin for CSS modules
18const cssModulesPlugin = {
19 name: "css-modules",
20 setup(build) {
21 build.onResolve({ filter: /\.module\.css$/ }, (args) => {
22 return {
23 path: path.resolve(args.resolveDir, args.path),
24 namespace: "css-module",
25 };
26 });
27
28 build.onLoad({ filter: /.*/, namespace: "css-module" }, (args) => {
29 const css = readFileSync(args.path, "utf8");
30 const classes = extractClassNames(css);
31 return {
32 contents: `export default ${JSON.stringify(classes)}`,
33 loader: "js",
34 };
35 });
36 },
37};
38
39// Use plugins
40await esbuild.build({
41 entryPoints: ["src/index.tsx"],
42 bundle: true,
43 outfile: "dist/bundle.js",
44 plugins: [stripConsolePlugin, cssModulesPlugin],
45});
HookPurposeUse Case
onResolveIntercept module resolutionCustom import paths, virtual modules
onLoadIntercept file loadingTransform content, CSS modules
onStartBuild start callbackClear output, log timing
onEndBuild end callbackWrite manifest, notify services
onDisposeCleanup on disposeClose file watchers
SWC — Rust-Based Compiler

SWC (Speedy Web Compiler) is a Rust-based platform for transpilation, bundling, and minification. It serves as a drop-in replacement for Babel with 20x+ faster performance and is the compiler behind Next.js and Deno.

terminal
Bash
1# Install SWC CLI
2npm install --save-dev @swc/cli @swc/core
3
4# Transpile a file
5npx swc src/index.js --out-file dist/index.js
6
7# Watch mode
8npx swc src -d dist -w
9
10# With configuration
11npx swc src -d dist --config-file .swcrc
.swcrc
JSON
1// .swcrc — SWC configuration
2{
3 "jsc": {
4 "parser": {
5 "syntax": "typescript",
6 "tsx": true,
7 "decorators": true,
8 "dynamicImport": true
9 },
10 "transform": {
11 "react": {
12 "runtime": "automatic",
13 "importSource": "react",
14 "refresh": true,
15 "pragma": "React.createElement"
16 },
17 "optimizer": {
18 "globals": {
19 "vars": {
20 "process.env.NODE_ENV": "production"
21 }
22 }
23 }
24 },
25 "target": "es2020",
26 "loose": false,
27 "externalHelpers": true,
28 "keepClassNames": true
29 },
30 "module": {
31 "type": "es6",
32 "strict": true,
33 "strictMode": true,
34 "lazy": false,
35 "noInterop": false
36 },
37 "minify": true,
38 "sourceMaps": true
39}

best practice

SWC's externalHelpers: true setting avoids inlining helper functions (like _class_call_check) in every file. Instead, they are imported from @swc/helpers, reducing bundle size. Install @swc/helpers as a dependency when enabling this.
SWC Loader Integration

SWC can replace Babel in Webpack, Vite, and other build tools via dedicated loaders.

integrations.js
JavaScript
1// Webpack — swc-loader replaces babel-loader
2module.exports = {
3 module: {
4 rules: [
5 {
6 test: /\.(js|jsx|ts|tsx)$/,
7 exclude: /node_modules/,
8 use: {
9 loader: "swc-loader",
10 options: {
11 jsc: {
12 parser: {
13 syntax: "typescript",
14 tsx: true,
15 },
16 transform: {
17 react: { runtime: "automatic" },
18 },
19 target: "es2020",
20 },
21 },
22 },
23 },
24 ],
25 },
26};
27
28// With Next.js (next.config.js)
29const nextConfig = {
30 swcMinify: true,
31 compiler: {
32 styledComponents: true,
33 removeConsole: process.env.NODE_ENV === "production",
34 },
35};
36
37module.exports = nextConfig;

info

SWC and esbuild are not mutually exclusive. Many projects use esbuild for bundling/pre-bundling (Vite uses it for deps) and SWC for transpilation (Next.js uses it for page compilation). Choose the right tool for each job.
Use Cases & Best Applications

Transpilation

Both tools excel at JSX/TypeScript to JavaScript transformation. SWC is the recommended choice for large TypeScript codebases where Babel has become a bottleneck.

benchmarks.txt
JavaScript
1// SWC transpilation benchmark (1000 TypeScript files)
2// Babel: ~45s
3// SWC: ~2.1s (21x faster)
4// esbuild: ~1.8s (25x faster)
5// tsc: ~52s (without declaration generation)

Minification

Both tools provide minifiers that are significantly faster than Terser while producing comparable output sizes.

minify.js
JavaScript
1// esbuild minifier (direct replacement for Terser)
2const esbuild = require("esbuild");
3
4// Minify a single file
5const result = await esbuild.build({
6 entryPoints: ["src/index.js"],
7 minify: true,
8 allowOverwrite: true,
9 outfile: "src/index.js",
10});
11
12// SWC minification via API
13const swc = require("@swc/core");
14const output = await swc.minify(code, {
15 compress: true,
16 mangle: true,
17 sourceMap: false,
18});
19
20// Both produce output ~5-10% larger than Terser
21// but finish 10-100x faster

Bundling (esbuild)

esbuild can be used as a standalone bundler for simple applications, libraries, and tools. It handles code splitting, CSS bundling, and asset loading.

build.js
JavaScript
1// Build a library with esbuild
2await esbuild.build({
3 entryPoints: ["src/index.ts"],
4 outfile: "dist/index.js",
5 format: "esm",
6 bundle: true,
7 minify: true,
8 sourcemap: true,
9 target: "es2020",
10 external: ["react", "react-dom"],
11 packages: "external",
12});
13
14// Bundling with CSS
15await esbuild.build({
16 entryPoints: ["src/index.tsx"],
17 outfile: "dist/bundle.js",
18 bundle: true,
19 loader: {
20 ".css": "css",
21 ".png": "file",
22 ".svg": "text",
23 },
24 assetNames: "assets/[name]-[hash]",
25});
Limitations & Trade-offs

While extremely fast, esbuild and SWC have important limitations that affect their suitability as full replacements for established tools.

LimitationesbuildSWCImpact
AST ModificationsLimited plugin hooksBetter but not Babel-levelCustom transforms harder
Plugin EcosystemSmall (400+ plugins)Small (200+ plugins)Less community tooling
Type CheckingNone (strips types)None (strips types)Requires separate tsc --noEmit
Code SplittingSupportedExperimentalSWC bundling still maturing
Ecosystem PluginsPolyfills, CSS-in-JSStyled Components, EmotionSome Babel plugins lack parity
ES Module OutputFirst-classFirst-classBoth support ESM well

warning

Neither esbuild nor SWC perform type checking. They strip TypeScript types without validating them. Always run tsc --noEmit as a separate step in your CI pipeline. This is by design — type checking requires full program analysis, which would negate the speed advantage.
esbuild vs. SWC Comparison
DimensionesbuildSWC
LanguageGoRust
Primary UseBundler + MinifierCompiler + Transpiler
BundlingExcellent (production-ready)Experimental (v1.3+)
TranspilationGood (no decorators, no legacy)Excellent (full Babel parity)
MinificationExcellent (mature)Excellent (mature)
Plugin APILimited (onResolve/onLoad only)Rich (Visitor-based AST transforms)
CSS SupportCSS bundling + minificationExperimental CSS modules
AdoptionVite, ESM, FigmaNext.js, Deno, Parcel
📝

note

SWC has richer AST transformation capabilities than esbuild, making it a better Babel replacement for projects that need custom transforms (like styled-components, emotion, or instrumentation). esbuild's plugin API is deliberately limited — it prioritizes speed over extensibility.
Integration with Vite & Next.js

The most common way developers encounter these tools is through framework integrations. Here is how they compose with modern frameworks:

integration-flow.txt
TEXT
1Vite (development)
2├── esbuild ────────────────── Pre-bundling dependencies
3│ ├── Convert CJS to ESM
4│ ├── Bundle multi-file deps into single file
5│ └── Support node_modules resolution
6├── esbuild ────────────────── Transpile TS/JSX
7│ └── .ts, .tsx, .jsx files
8└── Native ESM ─────────────── Serve to browser
9
10Vite (production)
11├── Rollup ─────────────────── Bundle application
12└── esbuild ────────────────── Minify bundle
13
14Next.js
15├── SWC ────────────────────── Transpile pages & components
16│ ├── JSX/TSX transformation
17│ ├── Styled Components transform
18│ ├── Remove console.log (configurable)
19│ └── Modularize imports
20├── SWC ────────────────────── Minify JavaScript
21└── Webpack / Turbopack ────── Bundle everything
framework-integration.js
JavaScript
1// Vite — esbuild is used internally (no configuration needed)
2import { defineConfig } from "vite";
3import react from "@vitejs/plugin-react";
4
5export default defineConfig({
6 optimizeDeps: {
7 // Control esbuild dep optimization
8 include: ["react", "react-dom", "lodash-es"],
9 exclude: ["@large-dep"],
10 esbuildOptions: {
11 target: "es2020",
12 },
13 },
14});
15
16// Next.js — SWC is the default compiler (no Babel needed)
17// next.config.js
18const nextConfig = {
19 // SWC is already the default
20 // Only disable if you have a .babelrc
21 swcMinify: true,
22 compiler: {
23 // Built-in SWC transforms
24 styledComponents: true,
25 emotion: true,
26 relay: {
27 src: "./src",
28 language: "typescript",
29 },
30 removeConsole: {
31 exclude: ["error"],
32 },
33 },
34};

best practice

When using Next.js, avoid creating a .babelrc file unless you have an explicit need. The presence of a Babel config disables SWC entirely, falling back to Babel for all compilation — which is 10-20x slower. Only add Babel if SWC does not support a required plugin.
Best Practices
Use esbuild for pre-bundling node_modules — it is 100x faster than Webpack
Prefer SWC over Babel for TypeScript/React transpilation in large projects
Always run tsc --noEmit separately since neither tool type-checks
Use esbuild for library bundling (simple, fast, ESM/CJS output)
Leverage Vite (uses esbuild) for new projects — it is the modern standard
Keep SWC's externalHelpers: true and install @swc/helpers as a dependency
Benchmark before migrating — performance gains vary by project structure
Do not use esbuild for production bundling if you need advanced code splitting or CSS-in-JS
Pair esbuild with Lightning CSS for production-grade CSS optimization
$Blueprint — Engineering Documentation·Section ID: BT-ESBUILD-01·Revision: 1.0