Transpilers convert modern JavaScript and TypeScript into backward-compatible code through a parse-transform-generate pipeline, with Babel using a JavaScript plugin architecture and SWC delivering 20x faster compilation through Rust-based native execution.
Babel parses source to AST, plugins visit and modify specific AST nodes during the transform phase, then the modified AST is printed back to JavaScript with source maps.
Syntax transforms convert new syntax to old equivalents at compile time; polyfills add missing runtime APIs like Promise and Array.from() that cannot be compiled away.
SWC reimplements Babel's functionality in Rust for 20x+ speedups, used by Next.js as default compiler, though with a smaller plugin ecosystem.
Babel's preset-env reads browserslist targets and enables only the plugins and polyfills needed for those specific environments, avoiding unnecessary transforms.
Transpilation converts source code written in one version of a language into another version. In the JavaScript ecosystem, this primarily means converting modern syntax (ES2015+ features, JSX, TypeScript) into code that older browsers or runtimes can execute. Understanding the transpilation pipeline is important for debugging unexpected behavior and making informed tooling decisions.
Babel processes code in three stages: Parse (source code becomes an AST using @babel/parser), Transform (plugins modify the AST — each plugin is a visitor that targets specific node types), and Generate (the modified AST is printed back to JavaScript using @babel/generator, optionally with a source map). This architecture makes Babel extremely flexible — any syntactic transformation can be implemented as a plugin.
Presets: Collections of Plugins
@babel/preset-env is the most important preset. Given a browser/Node.js target (via browserslist), it enables only the plugins needed to transform features not supported by those targets. For example, if your targets support arrow functions natively, Babel will not transform them. @babel/preset-react transforms JSX into React.createElement() calls (or the modern JSX transform that does not require importing React). @babel/preset-typescript strips type annotations — it does not type-check, it only removes types for faster compilation.
This is a crucial distinction. Syntax transforms convert new syntax to old syntax — Babel turns arrow functions into regular functions, optional chaining into ternary checks, etc. Polyfills add missing runtime APIs — Promise, Array.from(), Object.entries() do not exist in older engines and cannot be compiled away. Babel can inject polyfills via core-js and @babel/preset-env's useBuiltIns option: 'usage' adds polyfills only for features you actually use, 'entry' replaces a global import with the specific polyfills your targets need.
SWC: The Rust Alternative
SWC (Speedy Web Compiler) reimplements Babel's functionality in Rust, achieving 20x or greater speedups. It handles TypeScript stripping, JSX transformation, minification, and most common Babel plugins natively. Next.js replaced Babel with SWC as its default compiler starting in version 12. The tradeoff is extensibility — SWC supports a plugin system (written in WebAssembly), but the ecosystem of plugins is much smaller than Babel's.
There are two strategies for compiling TypeScript: Type-checking with tsc (the TypeScript compiler checks types and emits JavaScript), or Type-stripping with SWC/esbuild/Babel (types are removed without checking, and a separate tsc --noEmit step verifies types). The stripping approach is 10-50x faster because type-checking is the slow part — most modern setups use SWC or esbuild for fast compilation and run tsc --noEmit separately (often in parallel during CI).
Transpilers generate source maps that map positions in the output code back to positions in the original source. This enables debugging with the original code in DevTools, meaningful stack traces in error monitoring, and accurate code coverage reports. Source maps are JSON files with a mappings field containing VLQ-encoded position data.
Babel's strength is its plugin ecosystem and flexibility — any JavaScript developer can write a Babel plugin. SWC's strength is raw speed through native compilation. In practice, most projects no longer need Babel directly — frameworks like Next.js (SWC), Vite (esbuild), and Remix (esbuild) handle transpilation internally. Understanding the parse-transform-generate pipeline and the polyfill vs syntax transform distinction remains essential regardless of which tool performs the work.
Fun Fact
Babel was originally called '6to5' because it transpiled ES6 (ES2015) to ES5. When the project expanded to support plugins and arbitrary transformations beyond just ES6, Sebastian McKenzie renamed it to Babel — referencing the Tower of Babel, where languages were confused.