SWC (Speedy Web Compiler) is a super-fast JavaScript and TypeScript compiler written in Rust. It can compile, minify, bundle, and transform modern JavaScript and TypeScript at speeds 20-70x faster than Babel. Originally created by Donny (kdy1), SWC has become a foundational tool in the modern web ecosystem, powering Next.js, Parcel, Deno, and Vercel. Whether you need a drop-in Babel replacement, a high-performance minifier, or a fast TypeScript transpiler, SWC delivers the speed and correctness required by production build systems.
SWC is a Rust-based JavaScript/TypeScript compiler that is 20-70x faster than Babel. It handles JSX/TSX transpilation, TypeScript stripping, decorator transforms, module system conversion (ESM/CJS/UMD/AMD), minification via swc-minify, and bundling via swcpack. Configure it through .swcrc or swc.config.js. SWC powers Next.js (default since v12), integrates with Vite and Webpack via plugins, and provides a plugin system for custom AST transformations in Rust or Wasm. Migrating from Babel is straightforward with near-complete feature parity.
- SWC is written in Rust, leveraging zero-cost abstractions and parallelism to achieve 20-70x faster compilation than Babel.
- It supports JSX, TSX, TypeScript, decorators (legacy and 2024), and all modern ECMAScript features out of the box.
- The .swcrc configuration file provides fine-grained control over parsing, transformations, module output, and minification.
- SWC powers Next.js by default since version 12, replacing Babel for dramatically faster builds and HMR.
- swcpack is the built-in bundler that provides tree-shaking, code splitting, and module resolution similar to Webpack.
- The Wasm-based plugin system lets you write custom AST transformations that run at near-native speed.
Why SWC Is So Fast
SWC achieves extraordinary compilation speed through several architectural advantages inherent to Rust and its internal design.
- Written in Rust: Rust compiles to native machine code with zero-cost abstractions, no garbage collector, and deterministic memory management. This eliminates the runtime overhead that V8 introduces for JavaScript-based compilers like Babel.
- Parallel processing: SWC processes multiple files concurrently using Rust threads. Each file is parsed, transformed, and emitted independently, saturating all CPU cores during compilation.
- Single-pass transforms: SWC applies multiple transformations in a single AST traversal whenever possible, avoiding the repeated parse-transform-serialize cycles that Babel plugin chains require.
- Efficient memory layout: Rust structs and enums use compact, stack-allocated memory layouts. The AST representation is cache-friendly, reducing memory access latency compared to JavaScript object graphs.
# Benchmark: Compile 10,000 TypeScript files
#
# SWC 1.2s (Rust, parallel)
# esbuild 0.8s (Go, parallel)
# Babel 45.0s (JS, single-threaded)
# tsc 60.0s (TS, single-threaded)
#
# SWC is 37-50x faster than Babel/tsc
# for equivalent compilation tasksInstallation
SWC provides multiple packages depending on your use case. The core CLI package handles standalone compilation, while integration packages connect SWC to your existing build tools.
# Core SWC CLI
npm install --save-dev @swc/cli @swc/core
# Compile a single file
npx swc src/index.ts -o dist/index.js
# Compile a directory
npx swc src -d dist
# Watch mode
npx swc src -d dist --watch
# Integration packages
npm install --save-dev swc-loader # Webpack
npm install --save-dev @vitejs/plugin-react-swc # Vite
npm install --save-dev @swc/jest # Jest
# Verify installation
npx swc --version
# @swc/cli: 0.4.x
# @swc/core: 1.7.x.swcrc Configuration
The .swcrc file is the primary configuration mechanism for SWC. It controls parsing behavior, transformation rules, module output format, minification settings, and environment targets. Place it in your project root alongside package.json.
A basic .swcrc for a React TypeScript project:
// .swcrc — Basic React + TypeScript configuration
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": false,
"dynamicImport": true
},
"transform": {
"react": {
"runtime": "automatic",
"throwIfNamespace": true,
"useBuiltins": true
}
},
"target": "es2020",
"loose": false,
"externalHelpers": false
},
"module": {
"type": "es6"
},
"minify": false,
"sourceMaps": true
}An advanced .swcrc with decorators, module aliases, and environment targets:
// .swcrc — Advanced configuration with decorators and aliases
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true,
"dynamicImport": true,
"importAssertions": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true,
"react": {
"runtime": "automatic",
"importSource": "@emotion/react"
}
},
"target": "es2021",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"]
},
"externalHelpers": true
},
"module": {
"type": "commonjs",
"strict": true,
"lazy": true
},
"env": {
"targets": {
"chrome": "90",
"firefox": "88",
"safari": "14",
"node": "18"
},
"mode": "usage",
"coreJs": "3.36"
},
"sourceMaps": true
}Parsing and Syntax Support
SWC includes a full-featured JavaScript and TypeScript parser that supports all ECMAScript 2024 features, JSX, TSX, decorators (both legacy and TC39 stage 3), dynamic imports, top-level await, import assertions, and optional chaining. The parser is configurable through the jsc.parser section of .swcrc.
Parser configuration for different syntax modes:
// TypeScript parser with all features enabled
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true,
"dynamicImport": true,
"importAssertions": true
}
// JavaScript parser (ECMAScript)
"parser": {
"syntax": "ecmascript",
"jsx": true,
"decorators": true,
"decoratorsBeforeExport": true,
"dynamicImport": true,
"importAssertions": true,
"topLevelAwait": true,
"exportDefaultFrom": true
}Transformations
SWC applies code transformations based on target environment and configuration. Transformations include JSX compilation, TypeScript stripping, decorator lowering, class properties, optional chaining, nullish coalescing, and many other ECMAScript proposals.
JSX and TSX Transformation
SWC converts JSX syntax into function calls. It supports both the classic React.createElement runtime and the modern automatic JSX runtime introduced in React 17. The automatic runtime does not require importing React in every file.
// Input: JSX component
function App() {
return (
<div style={{ padding: 16 }}>
<h1>Hello SWC</h1>
<Button onClick={handleClick}>Click me</Button>
</div>
);
}
// Output with runtime: "automatic" (React 17+)
// SWC auto-inserts the jsx import
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function App() {
return _jsxs("div", {
style: { padding: 16 },
children: [
_jsx("h1", { children: "Hello SWC" }),
_jsx(Button, { onClick: handleClick, children: "Click me" })
]
});
}
// Output with runtime: "classic"
function App() {
return React.createElement("div", { style: { padding: 16 } },
React.createElement("h1", null, "Hello SWC"),
React.createElement(Button, { onClick: handleClick }, "Click me")
);
}Decorator Support
SWC supports both the legacy decorator specification (used by TypeScript experimentalDecorators) and the TC39 2024 decorator specification. The legacy mode is compatible with Angular, NestJS, MobX, and TypeORM. The 2024 spec is the standards-track version.
// Input: TypeScript class with legacy decorators
// Requires: "decorators": true, "legacyDecorator": true
import { Injectable, Controller, Get } from "@nestjs/common";
@Injectable()
class UserService {
findAll() {
return [{ id: 1, name: "Alice" }];
}
}
@Controller("/users")
class UserController {
constructor(private userService: UserService) {}
@Get()
getUsers() {
return this.userService.findAll();
}
}
// SWC compiles decorators to __decorate() calls
// compatible with TypeScript emitDecoratorMetadataClass Properties and Private Fields
SWC transforms class fields, static properties, and private fields (using # syntax) into compatible output for older environments. With useDefineForClassFields enabled, class fields use Object.defineProperty semantics matching the TC39 specification.
// Input: Modern class with all field types
class Config {
// Public field with initializer
apiUrl = "https://api.example.com";
// Private field (# syntax)
#secretKey = "abc123";
// Static field
static version = "2.0.0";
// Static private field
static #instances = 0;
constructor() {
Config.#instances++;
}
getSecret() {
return this.#secretKey;
}
}
// SWC transforms private fields to WeakMap
// when targeting older environmentsModule Systems
SWC can output code in four different module formats: ES Modules (ESM), CommonJS (CJS), UMD, and AMD. The module configuration determines how import/export statements are transformed in the output.
- ESM: ES Modules (es6): Preserves import/export syntax. Use for modern bundlers, browsers with module support, and Node.js with type: "module".
- CommonJS: CommonJS (commonjs): Transforms to require/module.exports. Use for Node.js packages, Jest tests, and environments that do not support ESM.
- UMD: UMD (umd): Universal Module Definition that works in both browser globals and CommonJS. Use for library packages that must support multiple consumption patterns.
- AMD: AMD (amd): Asynchronous Module Definition for RequireJS and similar AMD loaders. Rarely needed in modern projects.
// Input: ES module syntax
import { readFile } from "fs/promises";
import path from "path";
export const loadConfig = async (file) => {
const content = await readFile(path.resolve(file), "utf-8");
return JSON.parse(content);
};
export default loadConfig;
// Output: CommonJS (module.type: "commonjs")
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _promises = require("fs/promises");
const _path = _interopRequireDefault(require("path"));
const loadConfig = async (file) => {
const content = await (0, _promises.readFile)(
_path.default.resolve(file), "utf-8"
);
return JSON.parse(content);
};
exports.loadConfig = loadConfig;
exports.default = loadConfig;
// Output: UMD (module.type: "umd")
(function(global, factory) {
if (typeof define === "function" && define.amd)
define(["exports", "fs/promises", "path"], factory);
else if (typeof exports !== "undefined")
factory(exports, require("fs/promises"), require("path"));
else {
var mod = { exports: {} };
factory(mod.exports);
global.myModule = mod.exports;
}
})(this, function(exports, _promises, _path) {
// ... module code
});Minification with SWC
SWC includes a built-in minifier (swc-minify) that is a Rust port of Terser. It performs dead code elimination, constant folding, identifier mangling, whitespace removal, and advanced optimizations like inlining and branch simplification. The minifier works for both JavaScript and supports source map generation.
Minification configuration options:
// .swcrc — Minification configuration
{
"jsc": {
"parser": { "syntax": "typescript", "tsx": true },
"target": "es2020",
"minify": {
"compress": {
"dead_code": true,
"drop_console": true,
"drop_debugger": true,
"pure_funcs": ["console.info", "console.debug"],
"passes": 2,
"toplevel": true,
"unused": true
},
"mangle": {
"toplevel": true,
"keep_classnames": false,
"keep_fnames": false
}
}
},
"minify": true
}
// CLI minification
// npx swc src/index.ts -o dist/index.min.js --minifyBenchmark comparison of minifiers:
# Minification benchmark: 1MB JavaScript bundle
#
# swc-minify 0.4s 152 KB output
# esbuild 0.3s 155 KB output
# terser 8.2s 148 KB output
# uglify-js 12.1s 150 KB output
#
# swc-minify is 20x faster than Terser
# with comparable output size (2-3% larger)Bundling with swcpack
swcpack is the built-in bundler for SWC that handles module resolution, tree-shaking, code splitting, and output generation. While still marked as experimental, it demonstrates the direction SWC is heading as a complete build tool. Configure swcpack through spack.config.js in your project root.
Basic swcpack configuration:
// spack.config.js — swcpack bundler configuration
const { config } = require("@swc/core/spack");
module.exports = config({
entry: {
web: __dirname + "/src/index.ts",
},
output: {
path: __dirname + "/dist",
name: "[name].bundle.js",
},
module: {},
options: {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
target: "es2020",
},
},
});
// Run the bundler
// npx spackIntegration with Next.js
Next.js has used SWC as its default compiler since version 12, replacing Babel entirely for most projects. This switch reduced Fast Refresh (HMR) speed by 3x and build times by up to 5x. Next.js automatically configures SWC through next.config.js, so you do not need a separate .swcrc file.
SWC configuration within next.config.js:
// next.config.js — SWC configuration in Next.js
/** @type {import("next").NextConfig} */
const nextConfig = {
// SWC is enabled by default since Next.js 12
// These options customize its behavior
compiler: {
// styled-components support
styledComponents: true,
// Emotion CSS-in-JS support
emotion: {
sourceMap: true,
autoLabel: "dev-only",
labelFormat: "[local]",
},
// Remove console.log in production
removeConsole: {
exclude: ["error", "warn"],
},
// Import path optimization
modularizeImports: {
"lodash": {
transform: "lodash/\{\{member}}",
},
"@mui/icons-material": {
transform: "@mui/icons-material/\{\{member}}",
},
},
// React Server Components configuration
reactRemoveProperties: true,
},
// TypeScript path aliases work automatically
// SWC reads tsconfig.json paths
};
module.exports = nextConfig;Next.js SWC features include: styled-components and emotion support, automatic React runtime, import path optimization via modularizeImports, server-side dead code removal via removeConsole, TypeScript path aliases, and experimental server actions compilation.
Integration with Vite
Vite can use SWC as its JavaScript/TypeScript transformer through the @vitejs/plugin-react-swc plugin. This replaces the default esbuild-based JSX transformation with SWC, providing better Babel compatibility (decorators, legacy class properties) while maintaining similar speed.
Vite configuration with SWC:
// vite.config.ts — Using SWC with Vite
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
export default defineConfig({
plugins: [
react({
// Enable TypeScript decorators
tsDecorators: true,
// Custom SWC plugins
plugins: [
["@swc/plugin-styled-components", {}],
],
}),
],
build: {
target: "es2020",
sourcemap: true,
},
});
// Installation:
// npm install --save-dev vite @vitejs/plugin-react-swc
// npm install --save-dev @swc/plugin-styled-components (optional)Integration with Webpack
The swc-loader package provides a drop-in replacement for babel-loader in Webpack projects. It uses SWC for JavaScript and TypeScript compilation while keeping Webpack as the bundler. This is the fastest migration path for existing Webpack projects.
Webpack configuration with swc-loader:
// webpack.config.js — Using SWC with Webpack
const path = require("path");
module.exports = {
entry: "./src/index.tsx",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.(ts|tsx|js|jsx)\$/,
exclude: /node_modules/,
use: {
loader: "swc-loader",
options: {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
decorators: true,
},
transform: {
react: {
runtime: "automatic",
},
},
target: "es2020",
},
},
},
},
],
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"],
},
};
// Migration from babel-loader:
// 1. npm uninstall babel-loader @babel/core @babel/preset-env
// 2. npm install --save-dev swc-loader @swc/core
// 3. Replace "babel-loader" with "swc-loader" in webpack configPlugin System
SWC provides a Wasm-based plugin system that allows custom AST transformations. Plugins are written in Rust, compiled to WebAssembly, and loaded at runtime. This enables custom syntax transforms, code instrumentation, and compile-time code generation while maintaining near-native performance.
Creating a basic SWC plugin:
// Rust SWC plugin example (lib.rs)
// Cargo.toml dependency: swc_plugin_macro, swc_ecma_ast, swc_ecma_visit
use swc_plugin_macro::plugin_transform;
use swc_ecma_ast::Program;
use swc_ecma_visit::{VisitMut, VisitMutWith};
struct ConsoleRemover;
impl VisitMut for ConsoleRemover {
fn visit_mut_call_expr(
&mut self,
call: &mut swc_ecma_ast::CallExpr,
) {
// Visit children first
call.visit_mut_children_with(self);
// Custom transformation logic here
// e.g., remove console.log calls
}
}
#[plugin_transform]
fn process(program: Program, _config: String) -> Program {
let mut visitor = ConsoleRemover;
let mut program = program;
program.visit_mut_with(&mut visitor);
program
}
// Build: cargo build --target wasm32-wasi --releaseUsing plugins in .swcrc:
// .swcrc — Using custom and community plugins
{
"jsc": {
"parser": { "syntax": "typescript", "tsx": true },
"target": "es2020",
"experimental": {
"plugins": [
// Community plugin (npm package)
["@swc/plugin-styled-components", {
"displayName": true,
"ssr": true
}],
// Custom local plugin (.wasm file)
["./plugins/my-transform.wasm", {
"option1": "value1"
}],
// Emotion CSS-in-JS plugin
["@swc/plugin-emotion", {}]
]
}
}
}Migrating from Babel to SWC
Migrating from Babel to SWC is straightforward for most projects because SWC supports the majority of common Babel presets and plugins. The main steps are replacing babel-loader or @babel/cli with SWC equivalents and translating your Babel configuration to .swcrc format.
Step-by-step migration process:
# Step 1: Install SWC packages
npm install --save-dev @swc/core @swc/cli
# For Webpack projects:
npm install --save-dev swc-loader
# For Jest:
npm install --save-dev @swc/jest
# Step 2: Remove Babel packages
npm uninstall @babel/core @babel/cli @babel/preset-env
npm uninstall @babel/preset-react @babel/preset-typescript
npm uninstall babel-loader babel-jest
# Step 3: Create .swcrc from your babel config
# (see mapping table below)
# Step 4: Update package.json scripts
# Before: "build": "babel src -d dist"
# After: "build": "swc src -d dist"
# Step 5: Update Jest config (jest.config.js)
# Before: transform: { "^.+\\.tsx?\$": "babel-jest" }
# After: transform: { "^.+\\.tsx?\$": ["@swc/jest", {}] }
# Step 6: Delete .babelrc or babel.config.js
# Step 7: Test build and fix any edge casesCommon Babel preset and plugin mappings to SWC configuration:
| Babel Config | SWC Equivalent (.swcrc) |
|---|---|
| @babel/preset-env | jsc.target: "es2020" or env.targets |
| @babel/preset-react | jsc.transform.react.runtime: "automatic" |
| @babel/preset-typescript | jsc.parser.syntax: "typescript" |
| @babel/plugin-proposal-decorators | jsc.parser.decorators: true + jsc.transform.legacyDecorator |
| @babel/plugin-proposal-class-properties | Built-in (jsc.target controls output) |
| @babel/plugin-transform-runtime | jsc.externalHelpers: true |
| babel-plugin-module-resolver | jsc.baseUrl + jsc.paths |
| babel-plugin-styled-components | @swc/plugin-styled-components |
SWC vs Babel vs esbuild vs TypeScript Compiler
Understanding how SWC compares to other compilers helps you choose the right tool for your project.
| Feature | SWC | Babel | esbuild | tsc |
|---|---|---|---|---|
| Language | Rust | JavaScript | Go | TypeScript |
| Speed (10K files) | ~1.2s | ~45s | ~0.8s | ~60s |
| Type checking | No | No | No | Yes |
| Decorators (legacy) | Yes | Yes | No | Yes |
| Plugin system | Wasm plugins | JS plugins (huge ecosystem) | Go plugins (small) | Compiler API |
| Bundling | swcpack (experimental) | No | Yes (built-in) | No |
| Minification | Built-in (Terser-like) | Via babel-minify | Built-in | No |
| Framework adoption | Next.js, Parcel, Deno | Most (legacy) | Vite (dev), tsup | N/A (reference) |
SWC excels at fast compilation with high Babel compatibility, making it ideal for large projects migrating from Babel and for framework integration (Next.js). esbuild is fastest for pure bundling and simple transpilation. Babel remains necessary for projects requiring niche plugins not yet supported by SWC. tsc is the only option when you need type checking during compilation.
Best Practices
- Use SWC through your framework integration (Next.js, Vite, Parcel) when possible. Framework-level integration handles configuration automatically and ensures compatibility.
- Run tsc --noEmit separately for type checking. SWC strips TypeScript types but does not validate them, just like esbuild.
- Set the jsc.target option to match your minimum browser or Node.js support. This prevents unnecessary transformations for already-supported syntax.
- Enable the minifier with compress and mangle for production builds. SWC minification is comparable to Terser in output quality but significantly faster.
- Use ES module output format whenever possible. ESM enables better tree-shaking, static analysis, and compatibility with modern tools.
- For monorepos, share a base .swcrc configuration using the extends field to maintain consistency across packages.
- When migrating from Babel, test your output carefully. Some edge cases in decorator behavior or loose mode transforms may differ between Babel and SWC.
- Use the SWC playground (swc.rs/playground) to test configuration changes and see the transformation output before updating your build pipeline.
// Complete SWC production workflow (package.json)
{
"scripts": {
"dev": "swc src -d dist --watch --source-maps",
"build": "swc src -d dist --minify --source-maps",
"typecheck": "tsc --noEmit",
"test": "jest --config jest.config.js",
"lint": "eslint src --ext .ts,.tsx",
"ci": "npm run typecheck && npm run lint && npm run test && npm run build"
},
"devDependencies": {
"@swc/cli": "^0.4.0",
"@swc/core": "^1.7.0",
"@swc/jest": "^0.2.0",
"typescript": "^5.6.0"
}
}Frequently Asked Questions
Why is SWC faster than Babel?
SWC is written in Rust, which compiles to native machine code with zero-cost abstractions and no garbage collector overhead. Babel runs in Node.js with JavaScript, which requires V8 JIT compilation, garbage collection pauses, and is single-threaded by default. SWC also applies multiple transformations in fewer AST passes, reducing redundant parsing and serialization.
Does SWC support TypeScript type checking?
No. SWC strips TypeScript type annotations during compilation but does not perform type checking. This is a deliberate design choice for speed. You should run tsc --noEmit separately in your CI pipeline or development workflow to catch type errors.
Can SWC fully replace Babel?
For most projects, yes. SWC supports JSX, TypeScript, decorators (legacy and 2024), class properties, optional chaining, nullish coalescing, and most common Babel presets. However, some niche Babel plugins (like macros or custom syntax extensions) may not have SWC equivalents yet. Check the SWC documentation for current compatibility.
How does SWC work with Next.js?
Next.js has used SWC as its default compiler since version 12. Next.js automatically configures SWC based on your next.config.js settings. You do not need a separate .swcrc file. Next.js SWC integration includes styled-components support, import optimization, console removal, React automatic runtime, and experimental features like server actions.
What is swcpack and is it production-ready?
swcpack is the built-in bundler for SWC that handles module resolution, tree-shaking, and code splitting. As of 2026, it is still marked as experimental. For production bundling, most teams use SWC as a compiler within Webpack (via swc-loader), Vite (via plugin-react-swc), or Next.js rather than using swcpack directly.
How do I write an SWC plugin?
SWC plugins are written in Rust and compiled to WebAssembly. You create a Rust library that implements the visit_mut trait for AST transformation, compile it with swc_plugin_runner, and reference the .wasm file in your .swcrc configuration. The plugin API provides access to the full AST for custom transformations.
Is SWC compatible with existing Babel configuration?
SWC does not read .babelrc files directly. You need to translate your Babel configuration to .swcrc format. Common mappings include: @babel/preset-env maps to jsc.target, @babel/preset-react maps to jsc.transform.react, @babel/preset-typescript maps to jsc.parser.syntax: "typescript", and @babel/plugin-proposal-decorators maps to jsc.parser.decorators: true.
Which should I choose: SWC or esbuild?
Choose SWC if you need high Babel compatibility (decorators, legacy class properties, specific transform behaviors), are using Next.js, or need the Wasm plugin system for custom transforms. Choose esbuild if you need the absolute fastest bundling speed, a simpler configuration model, or are building a library with straightforward requirements. Both are excellent choices for modern JavaScript toolchains.