FRONTEND2026-04-09📖 7 min read

Turbopack vs Webpack: A Practical Comparison for Speeding Up Next.js Development

Turbopack vs Webpack: A Practical Comparison for Speeding Up Next.js Development

A practical comparison of Next.js's new bundler Turbopack and Webpack — covering development speed, configuration, and migration cost — with benchmark data and configuration examples to guide the best choice for your project.

髙木 晃宏

代表 / エンジニア

👨‍💼

Slow dev server startup. HMR (Hot Module Replacement) that takes a few seconds to reflect changes in Next.js projects. These pains are familiar to a lot of engineers. As projects grow — as components and pages multiply — the problem gets worse. When you consider that those few seconds between save and browser update happen hundreds of times a day, the impact on dev experience is larger than it might seem.

This article compares Next.js's two bundlers, Turbopack and Webpack, in practical terms and shares guidance on choosing the right one for your project. It goes beyond benchmark comparisons to cover the real challenges we faced in production and the troubleshooting we did during migration — hopefully useful when you're weighing adoption yourself.

The Fundamental Difference Between Turbopack and Webpack

Webpack's Decade of History

Webpack has been the de-facto standard for frontend development since its 2014 debut. A rich plugin ecosystem and high customizability are its attractions, but as projects grow, build-speed degradation becomes a real concern. I've personally hit situations on projects with hundreds of components where dev server startup took 30+ seconds.

The reason Webpack spread so widely is the landscape before it. Frontend development was loading multiple JavaScript files via <script> tags and suffering from global variable collisions. Webpack popularized the concept of module bundling, and that was a big contribution. Many things now taken for granted — Code Splitting, Tree Shaking, integrated processing of various assets — were opened up by Webpack.

The Arrival of Turbopack and Its Design Philosophy

Turbopack is a Rust-based bundler developed at Vercel by Tobias Koppers, the author of Webpack. It stabilized for the dev server in Next.js 15 and is available via next dev --turbopack. Its defining feature is an incremental computation engine that recomputes only changed modules and their dependencies.

Internally, Turbopack's "Turbo Engine" enables function-level fine-grained caching. If you change just the styling of one component, Webpack needs to rebuild the entire chunk containing that component, but Turbopack recomputes just the function result for the changed style processing and pulls the rest from cache. Thanks to this, HMR speed is proportional only to the blast radius of the change — not the total module count of the project.

A Fundamental Architectural Difference

The fundamental difference is this: Webpack processes the entire dependency graph in JavaScript, while Turbopack leverages Rust's parallelism to cache and recompute at the function level. That philosophical difference shows up most dramatically as speed differences in large projects.

A bit more concretely: when building the dependency graph, Webpack traces every import starting from the entry point and resolves modules file by file. This work happens in single-threaded JavaScript, so machines with many CPU cores don't benefit much. Turbopack uses Rust's multithreading to execute dependency resolution, transpilation, and bundling phases in parallel. On an 8-core machine, in theory, it can process 8 modules simultaneously.

What particularly struck me was Turbopack's "Lazy Compilation" behavior. Instead of compiling every page at dev server startup, it compiles only the routes the browser actually hits, on demand. Even in a 100-page project, it's common to touch only a handful during development. Thanks to this, startup is fast regardless of project size.

Measured Development Speed Comparison

Since this probably interests you, here are concrete numbers. Below are measurements from a Next.js 15 project with about 500 modules:

MetricWebpackTurbopackImprovement
First dev server startup28.4s4.2s~85% faster
HMR (component change)1.8s0.12s~93% faster
HMR (CSS change)0.9s0.06s~93% faster
Route first-access3.2s0.8s~75% faster

Vercel's official benchmarks report HMR up to 96% faster in large applications. My measurements confirm the trend — Turbopack's advantage grows the larger the module count.

Measurement Environment and Methodology

In fairness, here's the measurement environment. Machine: Apple M2 Pro (12-core, 32GB RAM). Node.js v20.11. Next.js 15.1. Each measurement is the median of 5 runs. To eliminate OS disk cache effects, we deleted node_modules/.cache and .next before each measurement.

HMR measurements used performance.now() to time from file save to the browser detecting the change. Specifically, we embedded a timestamp in a React component and calculated the diff in the HMR update event callback. More accurate than a manual stopwatch — try it in your own project.

Trends by Project Size

For small projects under 50 modules, Webpack startup tends to fit in 3-5 seconds anyway, so the perceived difference is smaller. I initially felt little benefit from Turbopack, then realized the gap widens as projects grow.

A more detailed breakdown by scale:

  • Small (~50 modules): Webpack is plenty comfortable. Turbopack's benefit is limited; either works, dev experience is similar
  • Medium (50-300 modules): Webpack HMR starts exceeding 1 second perceptibly. Switching to Turbopack gives you that "reflected the instant I saved" feeling, and the development rhythm noticeably changes
  • Large (300+ modules): Startup can take 20+ seconds with Webpack — this is the zone where Turbopack's benefits are greatest. On a 1,200-module project I worked on, Webpack startup was 45 seconds; Turbopack cut it to 6

Memory Usage and Resource Consumption

Memory usage — beyond just speed — matters for dev environment comfort. If you're developing inside Docker or on a spec-limited machine, the memory consumption differences are impossible to ignore.

With the same 500-module project, I compared memory during dev server operation:

MetricWebpackTurbopack
RSS just after startup~420MB~280MB
After 30 min of dev work~680MB~350MB
Peak during HMR~750MB~400MB

Turbopack's memory efficiency advantage comes from Rust's memory management model. Unlike Webpack which relies on JavaScript's garbage collection, Rust determines memory allocation and deallocation at compile time, so unused memory doesn't linger for long.

One caveat: Turbopack runs as a Rust binary and consumes memory separately from the Node.js process. When checking via top or Activity Monitor, sum both the Node.js process and the Turbopack process. I initially looked only at the Node.js process and thought "not much different"; summing them showed Turbopack is clearly lighter.

Configuration and Customization

Webpack's major strength is its mature plugin ecosystem. The webpack function in next.config.js lets you freely add loaders and plugins.

// next.config.js — Webpack configuration example module.exports = { webpack: (config, { isServer }) => { config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); return config; }, };

In Turbopack, you configure via the turbopack option in next.config.js. Webpack-compatible loaders are partially supported, but not every plugin works.

// next.config.js — Turbopack configuration example module.exports = { turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, resolveAlias: { '@components': './src/components', }, }, };

Honestly, the "should I switch?" dilemma is greater the more heavily you've customized Webpack. Use this checklist to decide:

Turbopack Migration Checklist

  • You're not using custom Webpack plugins
  • Your custom loaders are supported by Turbopack
  • You've taken inventory of your next.config.js webpack function configuration
  • You don't have Node.js-dependent special build processing
  • It's fine for your CI/CD next build to continue on Webpack (Turbopack's build is still stabilizing as of 2025)

Status of Major Loaders and Plugins

When considering migration, you'll want to know whether your usual loaders and plugins work with Turbopack. Here's the support status for what I've actually verified:

Loader/PluginTurbopack supportNotes
@svgr/webpackConfigurable via turbopack.rules
css-loader / postcss-loaderBuilt-in support
sass-loaderUsed via Next.js's sassOptions
babel-loaderSWC works as a replacement. Watch out for Babel-plugin dependencies
webpack-bundle-analyzerNo Turbopack equivalent currently
copy-webpack-pluginNeeds an alternative approach
DefinePluginCan be replaced via env settings

Even for plugins marked ✗, if your setup uses Turbopack for the dev server and Webpack for builds, it's usually fine. webpack-bundle-analyzer is build-time only, and copy-webpack-plugin often only needs to work during build.

CSS and Styling Support

Styling is unavoidable in frontend development. CSS processing is directly tied to bundler behavior, so it's worth extra attention during a Turbopack migration.

CSS Modules Behavior

CSS Modules work in both Turbopack and Webpack without issue. But there can be subtle behavioral differences. In one case I ran into, an inheritance pattern using CSS Modules' composes that worked in Webpack errored in Turbopack.

/* components/Button.module.css */ .base { padding: 8px 16px; border-radius: 4px; font-weight: 600; } .primary { composes: base; background-color: #0070f3; color: white; }

This basic composes is fine, but composes from another file (composes: base from './shared.module.css') can emit warnings due to file resolution order differences. If you hit this, rewriting to specify multiple class names instead of using composes is the reliable fix.

Compatibility with Tailwind CSS

For projects using Tailwind CSS, compatibility with Turbopack is excellent. PostCSS processing is supported natively, so Tailwind works without special configuration. In fact, Tailwind's JIT (Just-In-Time) mode and Turbopack's lazy compilation share a design philosophy, and the mechanism of generating only needed classes is a particularly good fit.

// postcss.config.js — same configuration works for Turbopack and Webpack module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, };

Cautions with CSS-in-JS Libraries

If you're using CSS-in-JS libraries like styled-components or Emotion, some care is needed. These libraries generate CSS at runtime, so they're less affected by bundler differences, but their SSR style extraction may include bundler-dependent code.

For styled-components, enabling Next.js's compiler.styledComponents option makes it work identically in both Turbopack and Webpack. But if your setup depends on the Babel plugin (babel-plugin-styled-components), you'll need to rewrite to SWC-based settings.

TypeScript and Path Alias Differences

A note on TypeScript project behavior. Basic type checking and transpilation work fine in both, but there are differences worth knowing.

Integration with tsconfig.json Paths

The paths setting in tsconfig.json is recognized by both. But where Webpack uses tsconfig-paths-webpack-plugin to resolve path aliases, Turbopack resolves paths directly from Next.js's configuration.

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"], "@components/*": ["./src/components/*"], "@lib/*": ["./src/lib/*"] } } }

With just this, Turbopack migration virtually never causes issues. If you've set custom resolve.alias inside next.config.js's webpack function, though, you need the equivalent turbopack.resolveAlias.

Detection Timing for Type Errors and Build Errors

In Webpack's dev server, type errors don't prevent compilation — they show as overlays in the browser. Turbopack behaves the same, but the overlay appears faster, making it easier to notice simple mistakes like typos immediately. It might sound minor, but faster "notice error → fix → confirm reflection" cycles compound meaningfully over daily development.

Staged Migration Strategy and Cautions

There's no one-size-fits-all on Turbopack migration, but here's a staged approach that worked in practice.

Step 1: Switch only the dev server

Just change the dev script in package.json. Production build stays on Webpack, making this a low-risk option.

{ "scripts": { "dev": "next dev --turbopack", "build": "next build" } }

Step 2: Verify compatibility

In the dev server, go through the main screens and check for layout issues or errors. CSS Modules, Sass, and image imports especially warrant verification. If issues come up, add loaders via turbopack settings, or roll back to Webpack temporarily while investigating.

More concrete checkpoints to verify:

  • Dynamic imports (next/dynamic): Lazy-loaded components render correctly
  • Image optimization (next/image): Local image import paths resolve correctly
  • Fonts (next/font): Google Fonts and local font CSS generation works
  • API Routes / Route Handlers: Server-side code runs without errors
  • Middleware (middleware.ts): Redirects and auth work as expected

Step 3: Roll out to the whole team

If no issues, switch the team's dev environment to Turbopack. Looking back, trying it on a smaller project first before applying to the main project — though it seems like a detour — was the most reliable approach.

When rolling out to the team, alongside the package.json change, add a line to the README or team docs. Just documenting the fallback — "if Turbopack doesn't work, run next dev (without --turbopack) for the traditional startup" — dramatically reduces confusion when issues arise.

Note: as of April 2025, Turbopack support for next build is still beta. Continuing Webpack for production builds and waiting for Turbopack to stabilize is a practical call.

Real Problems Encountered and Their Solutions

The explanation so far may still leave you wondering "does this actually work?" Here are issues I hit during real Turbopack migrations and how I resolved them.

Problem 1: Module not found Errors

This was the error I hit most immediately post-migration. Module paths that Webpack resolved implicitly were resolved strictly in Turbopack.

For example, extension-less imports that worked in Webpack errored in Turbopack.

// Works in Webpack but may error in Turbopack import { utils } from './helpers'; // Explicit extension resolves it import { utils } from './helpers/index';

The fix: review TypeScript's moduleResolution setting and make import paths explicit. The fix works in Webpack too, so it's also a code-quality improvement.

Problem 2: Environment Variable Timing

I thought .env.local variables were loading at a different time in Turbopack. Investigation showed that Next.js actually loads the env variables (not the bundler), so timing doesn't depend on the bundler — but because HMR is so fast, post-change reflection was "too fast to notice." Webpack required a restart; Turbopack reflected immediately. That's a benefit, not a problem, but confusing at first.

Problem 3: Third-Party Library Compatibility

I hit cases where a specific third-party library didn't work correctly in Turbopack's dev server. In my case, a charting library relied on the browser global window, and Turbopack's SSR processing threw window is not defined.

// Code that caused issues import Chart from 'some-chart-library'; // Solved by switching to dynamic import via next/dynamic import dynamic from 'next/dynamic'; const Chart = dynamic(() => import('some-chart-library'), { ssr: false });

This solution is a valid best practice in Webpack too — so you could say the Turbopack migration was an opportunity to improve the code.

CI/CD Pipeline Integration Considerations

When adopting Turbopack in development, you should also think through the CI/CD pipeline relationship.

A Configuration Where Dev and Build Use Different Bundlers

As mentioned, as of April 2025 the realistic configuration is "Turbopack for dev, Webpack for build." Under this setup, the thing to watch out for is that code that works fine in development may error at build time (or vice versa).

My team handles this by embedding two checks in CI:

# GitHub Actions example jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - name: Type Check run: npx tsc --noEmit - name: Build Check (Webpack) run: npm run build

Splitting type checking and build checking makes it easier to distinguish bundler-origin errors from type errors.

Preparing for a Future Turbopack Build

To make migration smooth when Turbopack builds reach stability, I'd recommend organizing next.config.js settings into Webpack-specific ones and settings common to both. Specifically, reduce custom configuration inside the webpack function as much as possible, bringing it closer to a form expressible via Next.js built-ins or turbopack options — that lowers the future migration cost.

Summary: Pick Based on Project Characteristics

Turbopack has the potential to dramatically improve dev experience on medium-to-large projects. Meanwhile, Webpack's rich ecosystem and stability remain reliable for projects that need complex configuration.

Given what we've covered, guidance on how to decide:

Cases where you should actively adopt Turbopack:

  • New Next.js 15 projects
  • Projects with little or no custom Webpack configuration
  • Projects where module count exceeds 100 and HMR slowness is affecting productivity
  • Projects using standard styling approaches like Tailwind CSS or CSS Modules

Cases where you should stay on Webpack:

  • Projects heavily dependent on custom plugins not yet supported by Turbopack
  • Projects requiring custom code transformations via Babel plugins
  • Projects where build-time bundle analysis (e.g., webpack-bundle-analyzer) is operationally essential

My practical take: aggressively adopt Turbopack in new projects or simple-configuration projects; migrate gradually in existing projects with heavy custom configuration. That's the right balance.

Turbopack is still evolving, and each release widens its supported surface. Even if a full migration isn't possible today, switching just the dev server is a reliable way to improve daily dev experience. How about starting by just trying next dev --turbopack?

If you're thinking about speeding up frontend development or choosing technologies for a Next.js project, feel free to reach out via Contact. We'll propose the right configuration tailored to your project.