· esbuild / swc / typescript
esbuild vs SWC 2026 — The Right Compiler for Your JS Stack
In 2026, SWC won the ecosystem bet; esbuild won the standalone speed crown. The decision framework for teams migrating from Babel or starting fresh.
By Ethan · Updated May 21, 2026
2,243 words · 12 min read
Pick SWC if you’re on Next.js, migrating a webpack project, or betting on Turbopack. Pick esbuild if you need a full bundler for a greenfield project, a library, or a CLI — no custom Babel plugins, maximum speed. If you’re on Vite, the answer in 2026 is neither: Rolldown handles dep pre-bundling, with an optional SWC plugin for React transforms.
That’s the verdict. The rest of this article is the reasoning — benchmarks, plugin ecosystem trade-offs, and migration cost — so you can judge whether your situation fits.
Who this is for
Developers choosing between esbuild and SWC for TypeScript/JSX transpilation or bundling — especially those migrating from Babel, or picking a toolchain for a new project. If you’re a Vite user, skip to the Vite section.
What we tested
esbuild v0.28.0 and @swc/core v1.15.33, both current as of May 2026. Benchmark numbers come from primary sources — esbuild’s own FAQ, SWC’s official perf blog, and Vercel’s engineering posts — unless stated otherwise. Surrounding ecosystem versions where relevant: Next.js v16, Vite v8.
The fundamental difference: bundler vs transformer
Before comparing speed, get the taxonomy right.
esbuild is a bundler that also transpiles. It resolves imports, code-splits, tree-shakes, and emits a production bundle. One tool, full pipeline.
SWC is a transformer only. It compiles TypeScript, JSX, and modern JavaScript — but it does not bundle. You still need a bundler (webpack, Rollup, Rolldown, or something else) on top.
This matters because “esbuild is faster than SWC” is not a useful comparison. You’re measuring different things. The right questions are: for the part where they overlap — transpiling TypeScript and JSX — how do they stack up? And separately: do you need a bundler at all?
Speed benchmarks
esbuild vs traditional bundlers
esbuild’s pitch is bundle speed. From the official esbuild FAQ, measured on a 10-entry-point benchmark:
| Tool | Build time |
|---|---|
| esbuild | 0.39s |
| Parcel 2 | 14.91s (38×) |
| Rollup 4 + Terser | 34.10s (87×) |
| webpack | 41.21s (106×) |
These numbers hold in practice. The speed comes from Go’s native parallelism, a shared AST parsing pass, and doing everything in a single traversal. esbuild does not have a plugin overhead for the built-in transforms.
SWC vs Babel
SWC’s benchmark (from swc.rs/blog/perf-swc-vs-babel) measures transform throughput at different concurrency levels:
| Concurrency | SWC (ops/sec) | Babel (ops/sec) | Ratio |
|---|---|---|---|
| 1 | 616 | 34.05 | 18.1× |
| 4 | 1,704 | 27.28 | 62.4× |
| 100 | 2,199 | 32 | 68.7× |
At 100 concurrent operations, SWC processes 2,199 files per second vs Babel’s 32. The gap widens with concurrency because SWC is Rust-native — the bottleneck for Babel is the V8 event loop.
For single-file transforms, the ratio is still 18.1×. Even in a serial build pipeline, that’s meaningful.
Next.js real-world gains
When Next.js 12 shipped SWC as its default compiler (replacing Babel), Vercel measured on their own production codebase:
- 17× faster transforms
- 3× faster Fast Refresh (hot reload)
- 5× faster builds
- 7× faster minification vs Terser
Turbopack (SWC-based, default bundler in Next.js 16 since October 2025) extends this further:
- up to 96.3% faster code updates with Fast Refresh (per the Turbopack stable release announcement)
These are not micro-benchmarks. They’re production measurements on a large Next.js application.
Plugin ecosystems in practice
Speed is one axis. The other is whether the tool supports what you actually need.
esbuild plugins
esbuild has a stable JavaScript plugin API. Plugins hook into the resolve and load phases. In practice, this covers the most common build needs:
- Path aliases (
@/components → src/components) - SVG imports (emit as React component or data URI)
- CSS modules
- Environment variable substitution
- Custom file loaders
The hard constraint: plugins cannot modify the AST. esbuild parses, processes, and emits — plugins intercept at file boundaries, not inside the syntax tree. For teams with custom Babel transforms that operate on the AST (macros, source-level rewriting, custom decorators), this is a blocker. You’d need to port that logic to a loader or find a different approach.
This is a deliberate design choice, not an oversight. It’s what lets esbuild keep its single-pass architecture. If you don’t need AST plugins, you don’t pay for the indirection.
SWC plugins
SWC has two plugin models that are worth keeping separate:
Built-in transforms (Rust, bundled with SWC): These are fast, well-tested, and cover the most common React-ecosystem needs:
- styled-components
- emotion
- Jest transform (
@swc/jest) - Class properties
- Decorators (stage 2 and 3)
- The Relay GraphQL compiler integration
For most React projects migrating from Babel, the built-ins cover everything. You get the SWC speed without touching Rust.
Wasm plugins (community-authored): You compile a Rust transform to WebAssembly. These let you perform arbitrary AST manipulation — but they are hard to author and even harder to maintain. Expect significant setup friction if you need a custom Wasm plugin, and verify that the Wasm compilation target is stable for your SWC version.
Where Babel still wins
If your project depends on a custom Babel macro, a domain-specific AST transform, or a Babel plugin that has no SWC equivalent, you’re still on Babel — or you have engineering work ahead. Both tools are narrowing this gap, but neither is at 100% Babel plugin parity.
Before committing to a migration, audit your Babel config and plugin list manually. Map each Babel plugin to its SWC equivalent or built-in — what has no equivalent is your risk surface.
Next.js users: SWC is already there
If you’re on Next.js 12 or later, SWC is your compiler by default. You didn’t pick it — it was picked for you.
One trap: if you have a .babelrc or babel.config.js in your project root, Next.js falls back to Babel automatically. This is a compatibility shim, not a recommendation. Babel fallback means you lose all the SWC speed gains. Audit your project root before assuming SWC is running. The Next.js build output logs SWC or Babel next to the compiler line — check it once.
Next.js 16 (October 2025) makes Turbopack the default bundler for all apps. Turbopack is built on SWC. Choosing SWC here means you’re aligned with the Vercel/Next.js roadmap, not swimming against it.
Vite users: the 2026 answer is Rolldown
Historically, Vite used esbuild for dependency pre-bundling (converting CJS node_modules to ESM quickly) and Rollup for production builds.
Vite v8 changed the dep pre-bundling to Rolldown — a Rust-based bundler built by VoidZero, the team behind Vite. esbuild is no longer in Vite’s critical path.
What this means in practice:
- Bundling: Rolldown handles it. You’re not choosing esbuild or SWC as your bundler.
- React transforms:
@vitejs/plugin-reactstill uses Babel under the hood by default. If you want SWC-based React transforms,@vitejs/plugin-react-swcis a drop-in swap — faster Fast Refresh in development, no Babel dependency.
If you’re on Vite, the “esbuild vs SWC” question mostly resolves to: do you want @vitejs/plugin-react-swc for faster dev-mode transforms? Usually yes, especially on larger component trees. But you’re not replacing your bundler either way.
If you’re evaluating whether to move off webpack entirely, Vite vs Webpack breaks down the migration cost and when the switch pays off.
Turbopack and future-proofing
Turbopack is Vercel’s Rust-based bundler, positioned as the webpack successor. It uses SWC as its core parser and transformer. With Next.js 16 defaulting to Turbopack for all builds, SWC is the transform layer underneath the next generation of the React ecosystem’s build tooling. If Turbopack vs Vite is a live question for your stack, Turbopack vs Vite covers the head-to-head HMR and build numbers.
esbuild’s trajectory is deliberately different. Evan Wallace has been explicit that esbuild’s feature scope is intentional — the goal is a stable, fast tool that does what it does without growing into everything. That predictability is a real asset for teams that want a build tool that doesn’t change APIs under them. But it also means esbuild won’t be the core of a Next.js-equivalent ecosystem integration.
If your stack is React + Next.js, SWC is the bet with the most tailwind. If your stack is greenfield or framework-agnostic, esbuild’s stability and simplicity are a real argument.
Migration cost: Babel → SWC vs Babel → esbuild
Babel to SWC
Lower friction for most projects:
- The
@swc/clireads a.swcrcthat maps closely to common Babel presets. - Built-in support for the most common Babel plugins: React, TypeScript, decorators, class properties, styled-components, emotion.
- Risk: custom Babel plugins with no SWC equivalent. Inventory them before migrating. If you have 3 custom plugins, expect one to need a workaround.
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true
},
"transform": {
"react": {
"runtime": "automatic"
}
},
"target": "es2022"
},
"module": {
"type": "es6"
}
}
This .swcrc handles TypeScript, TSX, decorators, and React’s new JSX transform. It’s roughly equivalent to @babel/preset-env + @babel/preset-react + @babel/preset-typescript + @babel/plugin-proposal-decorators.
Babel to esbuild
Higher architectural change, higher potential speed ceiling:
- esbuild replaces your bundler (webpack, Rollup), not only Babel. It’s a different scope of migration.
- AST-level Babel plugins are not portable.
- Best fit: library builds, CLI tools, serverless functions, or new greenfield projects where you control the full pipeline.
// build.mjs
import { build } from 'esbuild';
await build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
target: 'node20',
outfile: 'dist/index.js',
format: 'esm',
sourcemap: true,
});
This script bundles a TypeScript entry point for Node 20, emits ESM, and includes source maps. esbuild handles TypeScript stripping natively — no separate tsc pass needed (though you’ll still want tsc --noEmit for type checking in CI, since neither esbuild nor SWC does type checking).
When writing and iterating on build configurations like these, an AI-native editor like Cursor saves real time — the esbuild and SWC config APIs have a lot of surface area, and inline docs help avoid hunting through reference pages mid-migration.
Pick X if…
Pick esbuild if:
- You’re building a library, CLI, or serverless function from scratch
- You want one tool that handles both bundling and transpilation
- You have no custom Babel AST plugins (or can live without them)
- You don’t need webpack’s full plugin ecosystem
Pick SWC if:
- You’re on Next.js (it’s already your compiler — check you’re not accidentally falling back to Babel)
- You’re migrating a webpack project incrementally (SWC slots in as the compiler, webpack stays as bundler)
- You want alignment with the Turbopack roadmap
- You need styled-components or emotion transforms without extra setup
Use Rolldown + optional SWC plugin if:
- You’re on Vite v8+ — Rolldown handles bundling,
@vitejs/plugin-react-swchandles React transforms
Comparison table
| esbuild v0.28.0 | @swc/core v1.15.33 | |
|---|---|---|
| Primary role | Bundler + transpiler | Transpiler only |
| Written in | Go | Rust |
| TypeScript support | Yes (strips types; no type-check) | Yes (strips types; no type-check) |
| JSX | Yes | Yes |
| Decorators | Stage 3 only | Stage 2 + 3 |
| Plugin API | JS plugins (no AST mutation) | Rust/Wasm plugins + built-ins |
| styled-components | Via JS plugin | Built-in transform |
| emotion | Via JS plugin | Built-in transform |
| Vite integration | Replaced by Rolldown in v8+ | Optional via plugin-react-swc |
| Next.js default | No | Yes (since Next.js 12) |
| Turbopack | No | Yes (underlying parser) |
| npm downloads/week | ~71M | ~25M |
| Bundle speed | 0.39s (vs webpack 41.21s) | N/A — not a bundler |
| Transform speed vs Babel | N/A — no published benchmark | 18.1–68.7× (concurrency-dependent) |
Conclusion
In 2026, SWC has won the ecosystem bet. It’s the default compiler in Next.js, it underpins Turbopack, and it handles transform workloads at a speed that Babel cannot match at scale. If you’re anywhere near the Next.js stack, SWC is already your answer.
esbuild won the standalone speed crown. For greenfield projects that need a fast, self-contained bundler with no Babel legacy, it remains the cleanest choice. Its deliberate stability is a feature, not a gap — a build tool that doesn’t break APIs under you is worth something.
And for Vite shops: Rolldown is the real story in 2026. The esbuild vs SWC question mostly doesn’t apply — you’re choosing neither as your bundler, and @vitejs/plugin-react-swc is the relevant performance lever.
Caveats
- The esbuild 106× webpack benchmark comes from esbuild’s own FAQ, measured on esbuild’s own test workload. Your project may have a different shape.
- SWC’s 68.7× Babel ratio is at 100 concurrent operations. At a single operation, it’s 18.1×. Match the concurrency expectation to your actual build setup.
- Neither esbuild nor SWC does TypeScript type checking — you still need
tsc --noEmitin CI. - Vercel has an affiliate program. Toolchew does not currently have a tracked Vercel affiliate slug; Vercel is mentioned here because it’s the factual home of Next.js and Turbopack, not for affiliate reasons.
- This article contains an affiliate link to Cursor (
/go/cursor).