# How to Migrate Babel Projects to SWC: Configuration Differences and Compatibility Guide

> Easily migrate Babel projects to SWC. Learn configuration differences, map presets, and ensure compatibility with our simple guide. Switch to SWC today for faster builds.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: migration-guide
- Published: 2026-06-15

---

**Migrating from Babel to SWC requires replacing `.babelrc` or [`babel.config.js`](https://github.com/swc-project/swc/blob/main/babel.config.js) with a `.swcrc` JSON file, mapping presets to SWC's `env` and `react` fields, and selecting the appropriate `rootMode` to match Babel's configuration resolution behavior.**

SWC (Speedy Web Compiler) is a Rust-based TypeScript and JavaScript compiler maintained in the `swc-project/swc` repository that serves as a drop-in replacement for Babel. While both tools transpile modern JavaScript to older targets, they differ significantly in configuration schema, plugin architecture, and project root resolution. Understanding these differences is essential for preserving build behavior while gaining SWC's performance benefits.

## Configuration File Location and Resolution

Babel and SWC use different strategies for locating and loading configuration files.

**Babel** searches for [`babel.config.js`](https://github.com/swc-project/swc/blob/main/babel.config.js) (project-wide) or `.babelrc` (per-directory) relative to the file being compiled, merging multiple `.babelrc` files when traversing directories.

**SWC** uses `.swcrc` (JSON format) and implements three distinct **root modes** that control how the compiler walks the file system:

- **`upward`** (default): Searches upward from the source file and stops at the first `.swcrc`. Errors if none is found.
- **`upward-optional`**: Searches upward but silently uses defaults if no config exists.
- **`downward`**: Mimics Babel's cascading behavior by allowing multiple configs to merge when traversing directories.

The resolution logic is implemented in [`crates/swc/src/config/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc/src/config/mod.rs) (lines 740-770), while the CLI entry point that wires this together lives in [`crates/swc_cli_impl/src/commands/compile.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_cli_impl/src/commands/compile.rs).

## Mapping Babel Presets and Plugins to SWC

The configuration schemas differ in structure and terminology. In Babel, you declare presets and plugins as arrays of strings; in SWC, equivalent features are nested under typed fields in the `jsc` object.

**Key mappings:**

- **@babel/preset-env** → `env` field with `targets` object
- **@babel/preset-react** → `jsc.transform.react` configuration
- **@babel/preset-typescript** → `jsc.parser.syntax: "typescript"`
- **Parser plugins** (JSX, dynamic imports, decorators) → `jsc.parser` boolean flags
- **Class properties / decorators** → `jsc.transform.legacyDecorator` and `jsc.transform.decoratorMetadata`

The complete TypeScript definitions for these fields are exported from [`packages/types/index.ts`](https://github.com/swc-project/swc/blob/main/packages/types/index.ts), which serves as the source of truth for valid configuration options.

### Parser and Transform Options

Enable specific ECMAScript features under the `jsc` section:

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": true,
      "dynamicImport": true,
      "decorators": true
    },
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true,
      "react": {
        "runtime": "automatic",
        "development": false
      }
    }
  }
}

```

Unlike Babel's plugin-based model, SWC implements transforms as built-in Rust modules. Custom plugins must be written as native Rust or WebAssembly modules using the API defined in `crates/swc_plugin`.

## Compatibility Limitations and Gaps

Not all Babel functionality has direct SWC equivalents. Plan for these gaps before migrating:

- **Flow typing**: SWC parses Flow syntax but does not emit Flow-specific transforms. Projects relying on Flow compilation must retain Babel for those files.
- **Experimental proposals**: SWC only implements proposals that have reached the ECMAScript specification (e.g., class properties, private methods). Stage 3 proposals require waiting for SWC support or maintaining Babel for those specific inputs.
- **Config merging**: Babel automatically merges multiple `.babelrc` files when traversing subdirectories. To replicate this behavior in SWC, you must set `"rootMode": "downward"` in your `.swcrc`.
- **Source maps**: Both tools support `sourceMaps: true|inline|both`, but SWC generates maps by default unless explicitly disabled. The generation logic resides in [`crates/swc_ecma_codegen/src/lib.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_ecma_codegen/src/lib.rs).

## Step-by-Step Migration Checklist

Follow these steps to transition your build pipeline while preserving semantic parity:

1. **Install dependencies**: Run `npm i -D @swc/cli @swc/core` to add the compiler and command-line interface.

2. **Create `.swcrc`**: Translate your Babel configuration into the SWC JSON schema, mapping presets to their equivalent fields.

3. **Select root mode**: If you previously used multiple `.babelrc` files for nested overrides, set `"rootMode": "downward"` to enable cascading config resolution.

4. **Update build scripts**: Replace `babel src --out-dir lib --extensions ".js,.jsx"` with `swc src -d lib --config-file .swcrc`.

5. **Verify output**: Use the `swc_estree_compat` crate to compare ASTs. Run the comparison script in [`crates/swc_estree_compat/tests/compare.sh`](https://github.com/swc-project/swc/blob/main/crates/swc_estree_compat/tests/compare.sh) to diff Babel and SWC outputs.

6. **Handle unsupported plugins**: Identify Babel plugins without SWC equivalents and either retain Babel for those file patterns or implement a custom Rust/WebAssembly plugin.

## Practical Configuration Examples

### Minimal `.swcrc` for React/TypeScript Projects

This configuration mirrors a typical Babel setup with React, TypeScript, and environment-specific targets:

```json
{
  "jsc": {
    "target": "es2017",
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "dynamicImport": true
    },
    "transform": {
      "react": {
        "runtime": "automatic",
        "refresh": false
      }
    }
  },
  "module": {
    "type": "commonjs"
  },
  "env": {
    "targets": {
      "chrome": "80",
      "node": "14"
    }
  },
  "sourceMaps": true,
  "rootMode": "upward"
}

```

### CLI Usage Comparison

Migrate your build scripts by replacing Babel commands with SWC equivalents:

```bash

# Original Babel command

npx babel src --out-dir lib --extensions ".js,.jsx,.ts,.tsx" --source-maps

# SWC equivalent

npx swc src -d lib --config-file .swcrc

```

The `--config-file` flag explicitly points to your configuration; if omitted, SWC automatically searches for `.swcrc` using the resolution algorithm in [`crates/swc/src/config/mod.rs`](https://github.com/swc-project/swc/blob/main/crates/swc/src/config/mod.rs).

### Programmatic API Migration

Update Node.js build scripts to use the `@swc/core` API, which mirrors Babel's synchronous transform signature:

```javascript
const { transformSync } = require("@swc/core");

// Previous Babel usage:
// const result = babel.transformSync(code, { presets: ["@babel/preset-react"] });

const result = transformSync(code, {
  jsc: {
    parser: { syntax: "ecmascript", jsx: true },
    target: "es2020",
    transform: {
      react: { runtime: "automatic" }
    }
  },
  sourceMaps: true
});

```

### Verifying AST Compatibility

Validate semantic equivalence by converting SWC's AST to Babel's format and comparing outputs:

```bash
cd crates/swc_estree_compat
./tests/compare.sh path/to/your/file.js

```

This script uses the conversion logic in [`crates/swc_estree_compat/src/convert.rs`](https://github.com/swc-project/swc/blob/main/crates/swc_estree_compat/src/convert.rs) to generate a side-by-side diff, ensuring your migration preserves runtime behavior.

## Summary

- **Replace configuration files**: Move from `.babelrc` or [`babel.config.js`](https://github.com/swc-project/swc/blob/main/babel.config.js) to a single `.swcrc` JSON file located at your project root.
- **Map presets carefully**: Translate `@babel/preset-env` to the `env` field, `@babel/preset-react` to `jsc.transform.react`, and enable TypeScript via `jsc.parser.syntax`.
- **Control resolution behavior**: Use `"rootMode": "downward"` to replicate Babel's cascading config merging, or stick with `"upward"` for isolated directory-specific configs.
- **Account for plugin gaps**: Flow transforms and Stage 3 experimental proposals require retaining Babel or writing custom Rust/WebAssembly plugins.
- **Verify before shipping**: Use the `swc_estree_compat` comparison tool to validate AST equivalence between Babel and SWC outputs.

## Frequently Asked Questions

### Can I use my existing [`babel.config.js`](https://github.com/swc-project/swc/blob/main/babel.config.js) with SWC?

No. SWC requires a `.swcrc` JSON file and does not execute JavaScript configuration files. You must manually translate your Babel presets and plugins into the equivalent SWC JSON schema, using the type definitions in [`packages/types/index.ts`](https://github.com/swc-project/swc/blob/main/packages/types/index.ts) as a reference for available options.

### How do I handle Babel plugins that don't exist in SWC?

For plugins without native SWC equivalents, you have three options: retain Babel for specific file patterns using a build tool that supports multiple loaders, wait for community implementation of the transform, or write a custom plugin using the Rust or WebAssembly plugin API defined in `crates/swc_plugin`.

### Does SWC support Flow type annotations?

SWC can parse Flow syntax without errors, but it does not strip Flow types or perform Flow-specific transforms during compilation. Projects using Flow must either continue using Babel for type stripping or use a separate tool like `flow-remove-types` in their build pipeline.

### Is SWC faster than Babel for all project sizes?

Yes. According to the `swc-project/swc` benchmarks, the Rust-based compiler is typically 2-5× faster than Babel for large codebases, with particularly significant improvements in projects containing extensive TypeScript or JSX transformation pipelines. The performance advantage comes from SWC's parallelized Rust implementation and lack of plugin startup overhead.