---
title: "SWC vs Babel"
slug: "swc-vs-babel"
category: "comparisons"
tags: ["comparisons", "swc", "babel", "transpilation", "javascript", "build-tools", "typescript"]
status: "stable"
last_updated: 2026-05-14
summary: "Use SWC for all new projects; use Babel only when you depend on plugins that have no SWC equivalent."
related: ["[[comparisons/vite-vs-webpack]]", "[[comparisons/eslint-vs-biome]]", "[[comparisons/typescript-vs-flow]]", "[[comparisons/jest-vs-vitest]]", "[[coding/typescript]]", "[[comparisons/yarn-vs-pnpm]]", "[[frontend/index]]"]
---

## 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 [[comparisons/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-typescript` or `@babel/preset-react` needed.
- Next.js default since 12: Next.js ships with SWC and disables it only if a custom `.babelrc` is present. Removing the Babel config restores the fast path.
- Jest via `@swc/jest`: replace `babel-jest` and `ts-jest` with `@swc/jest` for test transforms. Watch-mode re-runs feel instant.
- `@swc/core` API: programmatic transforms are straightforward. Plugins exist for common patterns.

```json
// .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 like `linaria`, `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-operator` or 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/core` and `@swc/jest`.
- Create a minimal `.swcrc` with 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 `.babelrc` is 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 `legacyDecorator` option; it covers many cases. Test against your actual entity decorators before committing.

## Related

- [[comparisons/vite-vs-webpack]]
- [[comparisons/eslint-vs-biome]]
- [[comparisons/typescript-vs-flow]]
- [[comparisons/jest-vs-vitest]]
- [[coding/typescript]]
- [[comparisons/yarn-vs-pnpm]]
- [[frontend/index]]
