Babel & TypeScript Compiler
Babel and the TypeScript compiler (tsc) are the two foundational tools for transforming modern JavaScript and TypeScript into code that runs across browsers and environments. Babel focuses on syntax transformation and polyfilling, while tsc provides type checking and full TypeScript compilation.
Understanding when to use each — and how they complement each other — is essential for any serious JavaScript project. The industry trend has shifted toward using SWC or esbuild for speed, but Babel and tsc remain the most battle-tested and widely understood tools.
Babel is a toolchain for converting ECMAScript 2015+ code into a backwards-compatible version of JavaScript. It consists of three core stages: parsing, transforming, and code generation.
| 1 | // Babel configuration — babel.config.js (project-wide) |
| 2 | module.exports = { |
| 3 | presets: [ |
| 4 | ["@babel/preset-env", { |
| 5 | targets: "> 0.25%, not dead", |
| 6 | useBuiltIns: "usage", |
| 7 | corejs: 3, |
| 8 | modules: false, // Preserve ES modules for tree shaking |
| 9 | }], |
| 10 | ["@babel/preset-react", { |
| 11 | runtime: "automatic", // React 17+ JSX transform |
| 12 | }], |
| 13 | "@babel/preset-typescript", |
| 14 | ], |
| 15 | plugins: [ |
| 16 | "@babel/plugin-transform-runtime", |
| 17 | "babel-plugin-styled-components", |
| 18 | ["@babel/plugin-proposal-decorators", { legacy: true }], |
| 19 | ], |
| 20 | }; |
| File | Scope | Use Case |
|---|---|---|
| babel.config.js | Project-wide (root) | Monorepos, shared config |
| .babelrc | File-local | Single package |
| .babelrc.js | File-local (JS) | Dynamic config |
info
Presets are collections of plugins. Babel applies plugins in order (first to last) and presets in reverse order (last to first). Understanding this ordering is critical for correct transformation.
| 1 | // Preset execution order: last to first |
| 2 | // 1. @babel/preset-typescript |
| 3 | // 2. @babel/preset-react |
| 4 | // 3. @babel/preset-env |
| 5 | |
| 6 | // Plugin execution order: first to last |
| 7 | // 1. @babel/plugin-transform-runtime |
| 8 | // 2. babel-plugin-styled-components |
| 9 | // 3. @babel/plugin-proposal-decorators |
| 10 | |
| 11 | module.exports = { |
| 12 | presets: [ |
| 13 | ["@babel/preset-env", { |
| 14 | targets: { |
| 15 | browsers: ["> 1%", "last 2 versions", "not dead"], |
| 16 | node: "current", |
| 17 | }, |
| 18 | modules: false, |
| 19 | loose: true, |
| 20 | bugfixes: true, // Opt-in for smaller output |
| 21 | }], |
| 22 | ["@babel/preset-react", { |
| 23 | runtime: "automatic", |
| 24 | importSource: "@emotion/react", |
| 25 | pragma: "React.createElement", |
| 26 | pragmaFrag: "React.Fragment", |
| 27 | }], |
| 28 | "@babel/preset-flow", |
| 29 | ], |
| 30 | }; |
| Preset | Purpose | Key Options |
|---|---|---|
| @babel/preset-env | Smart transpilation based on targets | targets, useBuiltIns, corejs, modules |
| @babel/preset-react | JSX and React transformations | runtime, importSource, pragma |
| @babel/preset-typescript | Strip TypeScript types (no type check) | isTSX, allExtensions, onlyRemoveTypeImports |
| @babel/preset-flow | Strip Flow type annotations | all, ignoreExtensions |
best practice
Babel only transforms syntax, not APIs. To use modern APIs (like Array.prototype.includes or Promise) in older browsers, you need polyfills provided by core-js. Babel integrates with core-js via useBuiltIns.
| 1 | // Three useBuiltIns strategies: |
| 2 | |
| 3 | // "entry" — Import ALL polyfills for target browsers |
| 4 | // You add this once in your entry file: |
| 5 | import "core-js/stable"; |
| 6 | import "regenerator-runtime/runtime"; |
| 7 | // Babel replaces these with specific imports for your targets |
| 8 | |
| 9 | // "usage" — Auto-import polyfills per-file (recommended) |
| 10 | // Babel detects when you use a modern API and imports the polyfill: |
| 11 | // Your code: |
| 12 | const p = new Promise((resolve) => resolve()); |
| 13 | // Babel adds: |
| 14 | require("core-js/modules/es.promise"); |
| 15 | require("core-js/modules/es.object.to-string"); |
| 16 | |
| 17 | // false — No auto-polyfilling (default) |
| 18 | // You must manually polyfill everything |
| 19 | |
| 20 | module.exports = { |
| 21 | presets: [ |
| 22 | ["@babel/preset-env", { |
| 23 | targets: "> 0.25%, not dead", |
| 24 | useBuiltIns: "usage", // <-- recommended |
| 25 | corejs: { version: 3, proposals: true }, |
| 26 | shippedProposals: true, // Include stage-4 proposals |
| 27 | }], |
| 28 | ], |
| 29 | }; |
| Strategy | Bundle Size | Complexity | Recommendation |
|---|---|---|---|
| false | Smallest | Simple | Modern browsers only |
| "entry" | Largest | Simple | Legacy browser support |
| "usage" | Optimized | Complex | Recommended |
warning
The TypeScript compiler (tsc) performs two critical functions: type checking and compilation to JavaScript. Unlike Babel, tsc validates types before emitting code, catching category errors at compile time.
| 1 | { |
| 2 | "compilerOptions": { |
| 3 | /* Project Setup */ |
| 4 | "target": "ES2022", |
| 5 | "module": "ESNext", |
| 6 | "moduleResolution": "bundler", |
| 7 | "lib": ["ES2022", "DOM", "DOM.Iterable"], |
| 8 | "jsx": "react-jsx", |
| 9 | "outDir": "./dist", |
| 10 | "rootDir": "./src", |
| 11 | "declaration": true, |
| 12 | "declarationMap": true, |
| 13 | "sourceMap": true, |
| 14 | |
| 15 | /* Strict Type-Checking */ |
| 16 | "strict": true, |
| 17 | "noUncheckedIndexedAccess": true, |
| 18 | "noImplicitOverride": true, |
| 19 | "noPropertyAccessFromIndexSignature": true, |
| 20 | |
| 21 | /* Module Resolution */ |
| 22 | "baseUrl": ".", |
| 23 | "paths": { |
| 24 | "@/*": ["src/*"], |
| 25 | "@components/*": ["src/components/*"], |
| 26 | "@utils/*": ["src/utils/*"] |
| 27 | }, |
| 28 | "resolveJsonModule": true, |
| 29 | "allowImportingTsExtensions": true, |
| 30 | "isolatedModules": true, |
| 31 | |
| 32 | /* Output Control */ |
| 33 | "removeComments": true, |
| 34 | "stripInternal": true, |
| 35 | "skipLibCheck": true, |
| 36 | "forceConsistentCasingInFileNames": true, |
| 37 | |
| 38 | /* Build Performance */ |
| 39 | "incremental": true, |
| 40 | "tsBuildInfoFile": ".tsbuildinfo" |
| 41 | }, |
| 42 | "include": ["src"], |
| 43 | "exclude": ["node_modules", "dist", "**/*.test.ts"] |
| 44 | } |
| Category | Option | Purpose |
|---|---|---|
| Strict | strict | Enable all strict type-checking options |
| Strict | noUncheckedIndexedAccess | Add undefined to indexed access types |
| Output | declaration | Generate .d.ts declaration files |
| Output | declarationMap | Source maps for .d.ts files (Go to Definition) |
| Module | moduleResolution | "bundler" for modern bundlers (Vite, Webpack) |
| Module | paths | Path aliases matching bundler config |
| Performance | incremental | Build incrementally for faster recompilation |
| Performance | skipLibCheck | Skip type-checking .d.ts files (faster) |
best practice
TypeScript project references allow you to structure a codebase into smaller, independently compilable projects. They enable incremental builds, faster CI, and better separation of concerns in monorepos.
| 1 | // Root tsconfig.json (composite project) |
| 2 | { |
| 3 | "compilerOptions": { |
| 4 | "composite": true, |
| 5 | "declaration": true, |
| 6 | "declarationMap": true, |
| 7 | "emitDeclarationOnly": true, |
| 8 | "incremental": true, |
| 9 | }, |
| 10 | "references": [ |
| 11 | { "path": "./packages/core" }, |
| 12 | { "path": "./packages/ui" }, |
| 13 | { "path": "./packages/utils" }, |
| 14 | { "path": "./apps/web" } |
| 15 | ] |
| 16 | } |
| 17 | |
| 18 | // packages/core/tsconfig.json |
| 19 | { |
| 20 | "compilerOptions": { |
| 21 | "composite": true, |
| 22 | "outDir": "./dist", |
| 23 | "rootDir": "./src", |
| 24 | "declaration": true, |
| 25 | "declarationMap": true, |
| 26 | }, |
| 27 | "include": ["src"] |
| 28 | } |
| 29 | |
| 30 | // apps/web/tsconfig.json |
| 31 | { |
| 32 | "compilerOptions": { |
| 33 | "composite": true, |
| 34 | "outDir": "./dist", |
| 35 | "rootDir": "./src", |
| 36 | }, |
| 37 | "include": ["src"], |
| 38 | "references": [ |
| 39 | { "path": "../../packages/core" }, |
| 40 | { "path": "../../packages/ui" } |
| 41 | ] |
| 42 | } |
| 43 | |
| 44 | // Build command — references build in dependency order |
| 45 | // tsc --build tsconfig.json |
| 46 | // This builds: core -> ui -> web (in parallel where possible) |
pro tip
| Dimension | Babel | tsc |
|---|---|---|
| Type Checking | None (strips types) | Full type checking |
| Speed | Fast (no type check) | Slower (full analysis) |
| Output Size | Smaller (target-based) | Larger (transpile per target) |
| Plugin Ecosystem | Massive (5000+ plugins) | None (no plugin system) |
| Polyfills | core-js integration | Manual (no auto polyfill) |
| Declaration Files | No (.d.ts generation) | Yes (with declaration: true) |
| JSX Transform | Yes (automatic runtime) | Yes (jsx: "react-jsx") |
| Best For | Application bundling | Library/publishing |
| 1 | // Recommended approach: Babel for builds, tsc for checks |
| 2 | |
| 3 | // package.json scripts |
| 4 | { |
| 5 | "scripts": { |
| 6 | "build": "babel src --out-dir dist", |
| 7 | "typecheck": "tsc --noEmit", |
| 8 | "ci": "tsc --noEmit && npm run build && npm test" |
| 9 | } |
| 10 | } |
| 11 | |
| 12 | // For libraries — use tsc for both checking and output |
| 13 | { |
| 14 | "scripts": { |
| 15 | "build": "tsc", |
| 16 | "typecheck": "tsc --noEmit", |
| 17 | "prepublish": "npm run build" |
| 18 | } |
| 19 | } |
best practice
SWC (Speedy Web Compiler) is a Rust-based drop-in replacement for Babel that offers 10-20x faster transpilation with near-complete Babel plugin parity.
| 1 | // SWC configuration (.swcrc) |
| 2 | { |
| 3 | "jsc": { |
| 4 | "parser": { |
| 5 | "syntax": "typescript", |
| 6 | "tsx": true, |
| 7 | "decorators": true, |
| 8 | "decoratorVersion": "2022-03", |
| 9 | "dynamicImport": true |
| 10 | }, |
| 11 | "transform": { |
| 12 | "react": { |
| 13 | "runtime": "automatic", |
| 14 | "refresh": true, |
| 15 | "development": false |
| 16 | }, |
| 17 | "constModules": { |
| 18 | "globals": { |
| 19 | "process.env.NODE_ENV": "production" |
| 20 | } |
| 21 | } |
| 22 | }, |
| 23 | "target": "es2021", |
| 24 | "externalHelpers": true, |
| 25 | "keepClassNames": true, |
| 26 | "minify": { |
| 27 | "compress": { |
| 28 | "arguments": false, |
| 29 | "booleans": true, |
| 30 | "drop_console": true, |
| 31 | "unused": true |
| 32 | }, |
| 33 | "mangle": true, |
| 34 | "format": { |
| 35 | "comments": false |
| 36 | } |
| 37 | } |
| 38 | }, |
| 39 | "module": { |
| 40 | "type": "es6", |
| 41 | "strict": true |
| 42 | } |
| 43 | } |
| Tool | Language | Relative Speed | Babel Compat | Type Checking |
|---|---|---|---|---|
| Babel | JavaScript | 1x (baseline) | 100% | None |
| SWC | Rust | 10-20x | 95% | None |
| esbuild | Go | 20-30x | 70% | None |
| tsc | TypeScript | 0.2-0.5x | N/A | Full |
note