VOOZH about

URL: https://www.sitepoint.com/turbopack-in-production-nextjs-15-speed-wins/

โ‡ฑ Untitled


This metrics tool terrifies bad developers

Start free trial

This metrics tool terrifies bad developers

Start free trial
SitePoint Premium
Stay Relevant and Grow Your Career in Tech
  • Premium Results
  • Publish articles on SitePoint
  • Daily curated jobs
  • Learning Paths
  • Discounts to dev tools
Start Free Trial

7 Day Free Trial. Cancel Anytime.

JavaScript bundling has long been the performance ceiling for large Next.js projects. Slow hot module replacement, painful cold starts exceeding 10 seconds on sizeable codebases, and expensive CI builds that eat through compute minutes have defined the developer experience for years. Turbopack's stabilization in Next.js 15 delivers the largest measured speed improvement the framework has shipped since the App Router: Vercel's benchmarks report up to 76% faster cold starts compared to Webpack.

Turbopack is no longer hidden behind an experimental flag for development. As of Next.js 15, it ships as a stable, default-ready bundler for next dev, with active progress toward production build support. This tutorial covers the full path: enabling Turbopack, configuring it for real-world stacks, benchmarking actual performance gains, troubleshooting common breakages, and assessing production readiness.

Table of Contents

What Is Turbopack and Why It Matters

Turbopack vs. Webpack: Architecture at a Glance

Turbopack is a Rust-based incremental computation engine designed as Webpack's successor within the Next.js ecosystem. Where Webpack processes and bundles modules through a JavaScript-based pipeline, Turbopack uses Rust's performance characteristics alongside two key architectural decisions: function-level caching and lazy bundling. Function-level caching (where each transformation step is individually memoized by its inputs) means Turbopack remembers the result of every computation and only recomputes what has changed. Lazy bundling means it only processes modules actually requested by the browser, rather than eagerly building an entire dependency graph upfront.

This is distinct from tools like Vite, which uses esbuild for dependency pre-bundling and native ES modules for dev serving. Turbopack takes a different approach, operating as a unified incremental engine rather than composing multiple tools.

Function-level caching (where each transformation step is individually memoized by its inputs) means Turbopack remembers the result of every computation and only recomputes what has changed.

Stability Milestones in Next.js 15

Turbopack's journey began as an experimental flag in Next.js 13, matured through Next.js 14 with expanded test coverage, and reached stable status for next dev in Next.js 15. Turbopack is marked stable because it passed the Next.js integration test suite at the time of the Next.js 15 release. Check the Next.js 15 release notes for the exact pass-rate threshold and methodology. The next build --turbo path remains in active development. To check its current status, visit the Turbopack API reference or run npx next build --turbo and inspect the warning banner in the output. If still experimental, Next.js prints a warning; if stable, no warning appears.

Enabling Turbopack in Your Next.js 15 Project

Fresh Project Setup

Starting from scratch is the simplest path. Creating a new project and launching the dev server with Turbopack requires just two commands:

npx create-next-app@latest my-turbo-app
cd my-turbo-app
npx next dev --turbo

This is the zero-config happy path. Next.js 15's scaffolding produces a project structure fully compatible with Turbopack out of the box, with no additional configuration files or dependencies required.

Migrating an Existing Project

For existing projects, the migration involves updating the dev script in package.json and reviewing next.config.js for any Webpack-specific configuration that needs translation:

{
 "scripts": {
 "dev": "next dev --turbo",
 "build": "next build",
 "start": "next start",
 "lint": "next lint"
 }
}

If the project uses a next.config.js with custom Webpack configuration, those customizations will need to be migrated to Turbopack's configuration surface (covered in the configuration section below). Before running, verify the project meets the minimum requirements: Next.js 15 or later and Node.js 18.18.0 or later. Run npm ls next or yarn why next to confirm peer dependency alignment โ€” look for next@15.x.x in the output to confirm the installed version โ€” particularly if the project uses packages that pin specific Next.js versions.

Verifying Turbopack Is Active

After launching the dev server, the terminal output explicitly confirms which bundler is running. Look for this in the startup log:

 โ–ฒ Next.js 15.x.x
 - Local: http://localhost:3000
 - Turbopack ready

The presence of "Turbopack ready" (as opposed to the standard Webpack compilation messages) confirms the switch took effect. If the output shows "compiled successfully" with Webpack-style chunk information instead, Turbopack is not active and the --turbo flag may not be reaching the dev command correctly. Double-check the package.json script or pass the flag directly via the command line.

Benchmarking the Speed Wins

Cold Start Time

Measuring cold start time requires a controlled, reproducible approach. The following shell script compares startup times between Webpack and Turbopack by clearing caches and timing the initial server ready event:

#!/bin/bash
# benchmark-cold-start.sh
# Requires bash 4+. macOS users: brew install bash. Windows users: use WSL2.
# Run from project root only.
PORT=3099
WAIT_TIMEOUT=60000
clear_caches() {
 [ -d ".next" ] && rm -rf .next
 [ -d "node_modules/.cache" ] && rm -rf node_modules/.cache
}
run_timed() {
 local label="$1"
 local turbo_flag="$2"
 clear_caches
 echo "--- ${label} ---"
 npx next dev --port "$PORT" ${turbo_flag} &
 DEV_PID=$!
 START=$(date +%s%N)
 if ! npx wait-on --timeout "$WAIT_TIMEOUT" "http://localhost:${PORT}"; then
 echo "ERROR: dev server did not start within timeout." >&2
 kill "$DEV_PID" 2>/dev/null
 return 1
 fi
 END=$(date +%s%N)
 echo "${label}: $(( (END - START) / 1000000 ))ms"
 kill "$DEV_PID" 2>/dev/null
 wait "$DEV_PID" 2>/dev/null
}
run_timed "Webpack Cold Start" ""
run_timed "Turbopack Cold Start" "--turbo"

Vercel's published benchmarks report up to 76% faster cold starts with Turbopack compared to Webpack. This figure comes from Vercel's Turbopack benchmarks page (see turbo.build/pack/docs/benchmarks for methodology details, including project size and hardware specifications). Real-world results will vary depending on machine hardware, project size, dependency count, and operating system. Run the script above against your own project and compare to your Webpack baseline rather than treating 76% as a guaranteed figure.

Hot Module Replacement (HMR)

Turbopack's HMR speed advantage stems directly from its incremental computation model. When a developer edits a deeply nested component, Turbopack does not re-bundle the entire application or even the entire route. It recomputes only the functions affected by the change and sends a minimal update to the browser. In practice, this means editing a component five levels deep in a component tree typically produces a browser update in under 200ms for single-file edits, rather than the multi-second delay common in large Webpack projects.

To measure this yourself, open the browser devtools Network panel, filter by HMR events, edit a deeply nested component, and note the elapsed milliseconds between save and update. The difference is most noticeable on projects with hundreds of modules.

Editing a component five levels deep in a component tree typically produces a browser update in under 200ms for single-file edits, rather than the multi-second delay common in large Webpack projects.

Large Codebase Considerations

Speed gains from Turbopack scale non-linearly with project size. A small project with 20 routes and minimal dependencies may see under 20% improvement, because Webpack was already fast enough. Projects with 500+ routes, deep component trees, and a complex dependency graph are where Turbopack's lazy bundling and function-level caching show the largest gap. To quantify this for your codebase, run the cold-start benchmark on both a small route subset and the full project, then compare the ratios. The key factors are route count, component tree depth, and the total number of nodes in the dependency graph.

Configuring Turbopack for Your Stack

Custom Webpack Configurations: What Carries Over

Turbopack provides its own config keys under turbopack in next.config.js. This key requires Next.js 15. Verify your installed version with npm ls next to confirm the key is recognized โ€” if you see an "Invalid next.config.js option" warning on startup, consult the Next.js docs for your specific version's config structure. Common Webpack customizations like loader rules and path aliases translate to this new structure:

// next.config.js
const path = require('path');
/** @type {import('next').NextConfig} */
const nextConfig = {
 turbopack: {
 rules: {
 '**/*.svg': {
 loaders: ['@svgr/webpack'], // VERIFY: confirm @svgr/webpack Turbopack loader compat
 as: '*.js',
 },
 },
 resolveAlias: {
 '@components': path.resolve(__dirname, './src/components'),
 '@utils': path.resolve(__dirname, './src/utils'),
 },
 },
};
module.exports = nextConfig;

This example demonstrates two of the most common migration needs: handling SVG imports through a loader and configuring path aliases. The rules key replaces Webpack's module.rules for specifying loaders, while resolveAlias replaces resolve.alias. Note that @svgr/webpack is a Webpack-specific loader; verify its compatibility with Turbopack's loader interface before relying on it in your project. If issues arise, consider @svgr/rollup or a native Turbopack transform as alternatives. The resolveAlias values must be absolute paths โ€” use path.resolve(__dirname, '...') rather than bare relative strings, which resolve against an internal working directory and produce silent module-not-found failures.

Handling Unsupported Webpack Plugins

Turbopack does not support plugins that deeply hook into Webpack's compilation lifecycle, such as webpack-bundle-analyzer or custom plugins using compiler.hooks. For projects that depend on these during development, the graceful fallback is maintaining a second script in package.json:

{
 "scripts": {
 "dev": "next dev --turbo",
 "dev:webpack": "next dev"
 }
}

This allows developers to drop back to Webpack for specific edge cases without abandoning Turbopack as the default development experience.

Environment Variables and Feature Flags

Environment variable handling via .env, .env.local, .env.development, and .env.test files works identically under Turbopack. Turbopack resolves .env files in the same order as Webpack and handles the NEXT_PUBLIC_ prefix identically. That said, verifying environment variable availability in the browser after switching to Turbopack is a worthwhile smoke test, particularly for projects that rely on build-time variable inlining.

Production Readiness: Where Turbopack Stands Today

next dev --turbo vs. next build --turbo

The critical distinction: next dev --turbo is stable and tested. next build --turbo is in active development and may still carry experimental or alpha status. This means production builds currently still use Webpack. This split works fine in practice. The development bundler and the production bundler do not need to be the same tool, and Next.js has always maintained separate optimization paths for dev and build. Developers get Turbopack's speed during iteration while production output continues to use Webpack's mature optimization pipeline.

CI/CD Pipeline Adjustments

CI/CD configurations should use Turbopack for development server tasks (running tests against the dev server, smoke tests) while keeping the standard build for production artifacts. Ensure the dev script in package.json includes the --turbo flag (as shown in the migration section above), since Turbopack is activated via the CLI flag, not via environment variables:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
 test:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - uses: actions/setup-node@v4
 with:
 node-version: '18.18.0'
 cache: 'npm'
 - run: npm ci
 - run: | npm run dev -- --port 3099 &
 echo $! > /tmp/dev.pid
 shell: bash
 - run: npx wait-on --timeout 60000 http://localhost:3099
 - run: npm test
 - name: Cleanup dev server
 if: always()
 run: kill $(cat /tmp/dev.pid) 2>/dev/null || true
 build:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - uses: actions/setup-node@v4
 with:
 node-version: '18.18.0'
 cache: 'npm'
 - run: npm ci
 - run: npm run build

This workflow runs tests against the Turbopack dev server for speed while the production build step uses Webpack for stability. The dev server process PID is captured so cleanup runs reliably even if a prior step fails.

Testing Parity

Run your full test suite under Turbopack's dev server before committing to the switch. The goal is surfacing any bundler-specific differences. Common gotchas include dynamic imports that behave differently under lazy bundling (for example, a dynamic(() => import(...)) call may resolve in a different chunk order, causing hydration mismatches in tests), custom Babel transforms that need SWC equivalents, and CSS-in-JS libraries (particularly styled-components and Emotion) that require specific SWC transform settings in next.config.js rather than Babel plugins.

Troubleshooting Common Issues

"Module Not Found" After Enabling Turbopack

If you see Module not found: Can't resolve '@components/...' immediately after switching, the problem is almost always path alias resolution. Turbopack resolves aliases from tsconfig.json (or jsconfig.json) paths, but subtle differences in how wildcards and base URLs are interpreted can cause modules to go unresolved. Verify that tsconfig.json paths match exactly what Turbopack expects, including trailing /* patterns on directory aliases. Also ensure that any resolveAlias entries in next.config.js use absolute paths (via path.resolve(__dirname, '...')) rather than bare relative strings.

Styling Breakages

Check these in order: CSS Modules and Tailwind CSS v3.x work with Turbopack without additional configuration. Tailwind CSS v4 users should verify PostCSS plugin compatibility, as v4 uses a different configuration model. Sass requires confirming that the loader is configured under the turbopack.rules key in next.config.js. For styled-components or Emotion, verify that the SWC transform settings are enabled in next.config.js under the compiler key, as Turbopack uses SWC rather than Babel for these transforms.

Third-Party Package Incompatibilities

When a package fails silently or throws an unfamiliar error, enable diagnostic tracing first. Setting NEXT_TURBOPACK_TRACING=1 before launching the dev server produces trace logs that help identify which module or transform is failing. If no trace output appears, consult the Turbopack troubleshooting docs for the current variable name, as it may change across versions. Report confirmed incompatibilities to the Turbopack GitHub repository to help the team prioritize fixes.

Complete Implementation Checklist

  1. Confirm Next.js 15+ and Node.js 18.18.0+ are installed.
  2. Update the package.json dev script to next dev --turbo.
  3. Migrate custom Webpack loaders to the next.config.js turbopack key, verifying each loader's Turbopack compatibility individually.
  4. Verify Turbopack activation by checking for "Turbopack ready" in terminal output.
  5. Benchmark cold start and HMR against the Webpack baseline using timed runs.
  6. Run the full test suite under the Turbopack dev server and diff the results against your Webpack test output.
  7. Audit third-party packages for compatibility; enable NEXT_TURBOPACK_TRACING for diagnostics.
  8. Update CI/CD pipelines: Turbopack for dev/test steps, standard build for production.
  9. Monitor next build --turbo development progress for future full adoption.
  10. Document any fallback configurations and known incompatibilities for the team so the next person does not rediscover them.

Summary and Next Steps

Turbopack in Next.js 15 delivers measurable speed wins where they matter most: cold start times reduced by up to 76% according to Vercel's benchmarks (see turbo.build/pack/docs/benchmarks for methodology), sub-200ms HMR for single-file edits through incremental computation, and a noticeably faster feedback loop during development. Use Turbopack for development today, keep Webpack for production builds until next build --turbo reaches stable, and benchmark against your own project baselines rather than relying on generic numbers.

Use Turbopack for development today, keep Webpack for production builds until next build --turbo reaches stable, and benchmark against your own project baselines rather than relying on generic numbers.

Start here: run npx next dev --turbo, open your largest page, and time the cold start against your current Webpack baseline.

๐Ÿ‘ SitePoint Team
SitePoint Team

Sharing our passion for building incredible internet things.

SitePoint Premium
Stay Relevant and Grow Your Career in Tech
  • Premium Results
  • Publish articles on SitePoint
  • Daily curated jobs
  • Learning Paths
  • Discounts to dev tools
Start Free Trial

7 Day Free Trial. Cancel Anytime.

Stuff we do
Contact
About
Connect
Subscribe to our newsletter

Get the freshest news and resources for developers, designers and digital creators in your inbox each week

ยฉ 2000 โ€“ 2026 SitePoint Pty. Ltd.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Privacy PolicyTerms of Service