Overview
Use SWC for new JavaScript and TypeScript projects. It is 20 to 70 times faster than Babel on large codebases, supports TypeScript and JSX natively, and is the default transform in Next.js, Vite, and Bun. Use Babel only when you depend on plugins that SWC does not implement: Babel macros, babel-plugin-transform-decorators-legacy in its exact form, or custom company-specific AST transforms. See vite-vs-webpack for the bundler context.
When SWC wins
SWC is the right transpiler for nearly all new projects.
- Speed: SWC is written in Rust and compiles TypeScript and JSX in a single pass without type-checking. A 500-file project that takes 8 seconds with Babel compiles in under 400 ms with SWC. This difference compounds on watch rebuilds.
- TypeScript and JSX: SWC strips types and transpiles JSX natively. No
@babel/preset-typescriptor@babel/preset-reactneeded. - Next.js default since 12: Next.js ships with SWC and disables it only if a custom
.babelrcis present. Removing the Babel config restores the fast path. - Jest via
@swc/jest: replacebabel-jestandts-jestwith@swc/jestfor test transforms. Watch-mode re-runs feel instant. @swc/coreAPI: programmatic transforms are straightforward. Plugins exist for common patterns.
// .swcrc
{
"jsc": {
"parser": { "syntax": "typescript", "tsx": true },
"target": "es2020"
},
"module": { "type": "commonjs" }
}When Babel wins
Babel remains the correct choice when specific plugins are real dependencies.
- Babel macros (
babel-plugin-macros): compile-time code generation libraries likelinaria,twin.macro, and some CSS-in-JS tools rely on Babel macros. SWC does not support the Babel macro protocol. babel-plugin-transform-decorators-legacy: some ORMs and frameworks (TypeORM, older NestJS configs) require this exact plugin’s behavior. SWC’s decorator support follows the TC39 proposal, which behaves differently.- Custom AST plugins: teams with internal Babel plugins for code transforms (feature flags, i18n extraction) need those plugins ported or rewritten before switching.
- Partial coverage: if the codebase uses
@babel/plugin-proposal-pipeline-operatoror other Stage 2/3 proposals, check SWC’s plugin list first.
Trade-offs at a glance
| Dimension | SWC | Babel |
|---|---|---|
| Speed | 20 to 70x faster than Babel | Baseline; slower on large codebases |
| Language | Rust | JavaScript |
| TypeScript support | Native strip (no type-check) | Via @babel/preset-typescript |
| JSX support | Native | Via @babel/preset-react |
| Plugin ecosystem | Growing; covers common cases | Mature; thousands of plugins |
| Babel macros | Not supported | First-class |
| Decorators | TC39 proposal semantics | Legacy and proposal semantics |
| Next.js integration | Default since Next 12 | Opt-in via .babelrc (disables SWC) |
| Jest transform | @swc/jest | babel-jest |
| Custom AST transforms | SWC plugin API (Rust or JS WASM) | Plain JavaScript plugins |
| Config complexity | Low; .swcrc is concise | Moderate; multiple presets and plugins |
Migration cost
Babel to SWC migration is usually straightforward for standard configurations.
- Remove
babel-jest,@babel/core, and presets. Add@swc/coreand@swc/jest. - Create a minimal
.swcrcwith parser settings, target, and module type. - Run the test suite. Most failures are from macro-dependent code or decorator edge cases. Fix or exclude those files.
- For Next.js: simply delete
.babelrc. Next.js detects the absence and enables SWC automatically. - Effort: half a day for a project with no macros or legacy decorator patterns. Budget additional time per macro-dependent library, since those require finding a non-macro alternative or maintaining a Babel transform just for that file.
SWC to Babel is a regression. Do not do it unless forced by a specific plugin requirement.
Recommendation
- New project: SWC. Set it as the Jest transform and the Vite/bundler transform from the start.
- Next.js project: ensure no
.babelrcis present. SWC is the default and should stay the default. - Project using Babel macros: evaluate whether the macro library has a non-macro alternative (many do). If not, stay on Babel for that specific transform and use SWC elsewhere.
- TypeORM or legacy decorator project: check SWC’s
legacyDecoratoroption; it covers many cases. Test against your actual entity decorators before committing.