Module A-11·15 min read

Why Turbopack replaced Webpack, incremental computation model, SWC transforms, turbopack config and filesystem cache, module graph anatomy, diagnosing OOM build failures, and pageExtensions for non-standard project layouts.

Written by Jatin Jain Saraf · Architect of SupraScan

A-11 — Build Engine Internals: Turbopack, SWC, and Memory Optimisation

Who this is for: Architects who want to understand what happens between npm run build and the deployable output — how Turbopack replaced Webpack, why SWC is faster than Babel, how the module graph is constructed, and how to diagnose and fix build performance problems that appear when a codebase scales.


Why the Build Stack Changed

For four years, Next.js used Webpack 5 as its bundler and Babel as its JavaScript transformer. Both were fine at small scale. Both had known limitations at large scale:

Webpack's problem: Webpack rebuilds the module graph on every save. In a large application, the graph might have 10,000 modules. Even incremental builds re-analyse significant portions of this graph. Teams with large codebases saw 30-60 second hot reload times.

Babel's problem: Babel is written in JavaScript and processes files one-at-a-time. TypeScript type stripping, JSX transformation, decorator transforms — Babel does each file sequentially. SWC does the same transformations in Rust, parallelised across all CPU cores, 10-100x faster.

The replacement strategy: SWC for JavaScript/TypeScript transformation (already the default in Next.js 12+), Turbopack for bundling (stable in Next.js 15 for development, nearing stability for production).


SWC — The Transformation Layer

SWC (Speedy Web Compiler) is a Rust-based JavaScript/TypeScript compiler. It replaced Babel as Next.js's transform layer in version 12.

What SWC handles:

  • TypeScript → JavaScript (type stripping, not type checking)
  • JSX → React.createElement calls
  • ES2022+ → target ES version
  • Import path transforms (absolute imports, module aliases)
  • Emotion/styled-components transforms (when configured)
  • React Compiler transforms (when enabled)

What SWC does not do:

  • Type checking (that's tsc --noEmit, run separately in CI)
  • Bundling (that's Webpack or Turbopack)
  • Custom Babel plugins (Babel is no longer in the chain when SWC is active)

The custom Babel plugin problem: if your project uses a Babel plugin that SWC doesn't have a native equivalent for, you have to keep Babel in the chain — which means losing SWC's speed advantage for those transforms. This is why teams with custom Babel plugins see slower builds than teams that migrated fully.

// next.config.ts — disable SWC if you have incompatible Babel transforms
const config: NextConfig = {
  swcMinify: true,   // use SWC for minification (default true in Next.js 13+)
 
  // To keep Babel (opt-out of SWC):
  // Create .babelrc or babel.config.js — Next.js automatically falls back to Babel
  // when it detects a Babel config file
};

For styled-components and Emotion, SWC has built-in transforms that are faster than the Babel plugins:

// next.config.ts
const config: NextConfig = {
  compiler: {
    styledComponents: true,
    emotion: true,
    // removeConsole: { exclude: ['error'] }, // strip console.log in production
  },
};

Turbopack — The Bundler Replacement

Turbopack (also written in Rust) is the replacement for Webpack. Its architectural difference: incremental computation with fine-grained caching.

Webpack's model: build the entire module graph, apply transforms, produce bundles. Incremental builds re-analyse changed modules and their transitive dependents.

Turbopack's model: every module and every function on every module is a cacheable unit of computation. When a file changes, only the computation units that depend on that specific file are re-evaluated. The cache is persistent across restarts — a restart after Turbopack has warmed its cache is nearly as fast as a hot reload.

The practical result: a codebase that took 30 seconds for a hot reload with Webpack might take 500ms with Turbopack, because Turbopack doesn't re-evaluate the 9,800 modules that didn't change.

Enable Turbopack for development:

next dev --turbopack
# or in package.json:
# "dev": "next dev --turbopack"

Turbopack is the default for next dev in Next.js 15. For next build (production), Turbopack is still in progress — production builds use Webpack by default until Turbopack production build reaches parity.


Turbopack Configuration

Turbopack configuration lives in next.config.ts under the turbopack key:

// next.config.ts
const config: NextConfig = {
  turbopack: {
    // Custom module resolution aliases
    resolveAlias: {
      '@': './src',
      'lodash': 'lodash-es', // use ESM version of lodash
    },
 
    // Custom file extensions to resolve
    resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
 
    // Custom loaders (replaces Webpack loaders for Turbopack)
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
};

The Webpack loader compatibility note: Turbopack cannot use Webpack loaders directly. If your project uses custom Webpack loaders (SVG transforms, MDX loaders, etc.), you need to find or create Turbopack-compatible versions. This is the primary migration blocker for complex projects.


pageExtensions — Customising Which Files Are Routable

Next.js decides what counts as a page, layout, or route handler partly by file extension. The default list is ['tsx', 'ts', 'jsx', 'js'] — any file with one of those extensions in the right location (app/page.tsx, pages/about.js, etc.) is treated as routable. pageExtensions in next.config.ts lets you change that list:

// next.config.ts
const config: NextConfig = {
  pageExtensions: ['page.tsx', 'page.ts'],
};

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.