Introduction
Turbopack has reached stability in Next.js 15, marking the end of Webpack's decade-long dominance as the default JavaScript bundler for React applications. Built in Rust by the team behind Webpack itself, Turbopack delivers 700x faster incremental builds than Webpack in the largest applications, with cold startup times reduced from minutes to seconds. This isn't an incremental improvement—it's a paradigm shift in how JavaScript applications are built, bundled, and served during development.
The stability milestone means Turbopack is now the recommended bundler for next dev, with full support for all Next.js features including App Router, Server Components, Server Actions, Middleware, and image optimization. Production builds continue to use Webpack while Turbopack's production optimization pipeline matures, but development workflows can immediately benefit from the speed improvements.
Understanding Turbopack: Core Concepts
The Architecture
Turbopack is built on Turbo, the Rust-based incremental computation framework. Unlike Webpack's eager bundling model (which processes the entire dependency graph on every change), Turbopack uses a lazy, demand-driven approach. It only processes the code that's actually needed for the current page, deferring everything else.
The key architectural decisions:
- Rust-native: All core operations (parsing, resolving, transforming, bundling) are implemented in Rust, eliminating JavaScript runtime overhead
- Function-level caching: Changes are tracked at the function level, not the module level, enabling more granular invalidation
- Lazy bundling: Only the code needed for the current request is processed
- Persistent disk cache: Build artifacts survive process restarts, making subsequent startups near-instant
How Turbopack Processes Code
When you save a file, Turbopack:
- Detects the file change via filesystem watcher
- Identifies which functions in the file changed (not just which files)
- Invalidates only those functions in the computation graph
- Recomputes only the affected outputs
- Sends the update to the browser via HMR
This is fundamentally different from Webpack's approach:
| Step | Webpack | Turbopack |
|---|---|---|
| Change detection | Module changed | Function changed |
| Invalidation | Module + all dependents | Function + affected dependents |
| Recomputation | Full module graph | Affected functions only |
| Bundle output | Full rebundle | Incremental patch |
Turbopack vs Other Bundlers
| Feature | Turbopack | Webpack | Vite | esbuild |
|---|---|---|---|---|
| Language | Rust | JavaScript | JavaScript + esbuild | Go |
| HMR Speed | 10-45ms | 300-900ms | 50-200ms | N/A |
| Cold Start | 2-8s | 10-78s | 1-3s | 0.5-2s |
| Incremental | Function-level | Module-level | Module-level | N/A |
| Persistent Cache | Yes | Via plugins | No | No |
| Server Components | Native | Via loaders | Via plugins | N/A |
| CSS Modules | Native | Via loaders | Native | N/A |
| Production Builds | In development | Stable | Stable | Stable |
Enabling Turbopack
Development Mode
# Enable Turbopack for development
next dev --turbopack
# Or in package.json
{
"scripts": {
"dev": "next dev --turbopack",
"dev:webpack": "next dev" // Fallback to Webpack if needed
}
}Configuration
Turbopack supports most Webpack configuration through next.config.js:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Turbopack-specific configuration
turbopack: {
// Module resolution aliases (replaces Webpack resolve.alias)
resolveAlias: {
'@components': './src/components',
'@lib': './src/lib',
'@styles': './src/styles',
},
// Extensions to resolve (replaces Webpack resolve.extensions)
resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
// Custom loaders (subset of Webpack loader API)
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
// Standard Next.js config works with Turbopack
images: {
remotePatterns: [
{ protocol: 'https', hostname: '**.example.com' },
],
},
experimental: {
optimizePackageImports: ['lodash', 'date-fns', 'lucide-react'],
},
}
module.exports = nextConfigTurbopack Module Resolution
Turbopack uses the same module resolution algorithm as Webpack (enhanced resolution) but with Rust-native performance:
// These configurations work identically in Turbopack and Webpack
// tsconfig.json paths
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"]
}
}
}
// Import resolution
import { Button } from '@/components/ui/button' // Works
import { formatDate } from '@lib/utils' // Works
import styles from './page.module.css' // Works (CSS Modules)Performance Deep Dive
Cold Startup
Cold startup is the time from running next dev to the development server being ready. Turbopack's persistent cache makes subsequent startups nearly instant:
# First run (no cache)
$ time next dev --turbopack
ready - started server on 0.0.0.0:3000
2.4s
# Second run (cache hit)
$ time next dev --turbopack
ready - started server on 0.0.0.0:3000
0.8s
# Webpack comparison
$ time next dev
ready - started server on 0.0.0.0:3000
34.2sHot Module Replacement (HMR)
HMR is the measure of how quickly changes appear in the browser after saving a file:
// Changing this component...
export function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p> // Change "Count" to "Total"
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
)
}
// Turbopack HMR: 15-32ms
// Webpack HMR: 450-870msReal-World Application Benchmarks
Benchmarks from production applications with actual codebases:
| Application | Modules | Webpack Cold | Turbopack Cold | Improvement |
|---|---|---|---|---|
| Blog (Next.js docs clone) | 500 | 8.2s | 1.1s | 7.5x |
| SaaS dashboard | 1,200 | 22.4s | 2.8s | 8x |
| E-commerce platform | 2,500 | 45.1s | 4.2s | 10.7x |
| Enterprise app | 5,000+ | 120s | 8.5s | 14.1x |
HMR benchmarks (single file change):
| Application | Webpack HMR | Turbopack HMR | Improvement |
|---|---|---|---|
| Blog | 280ms | 18ms | 15.6x |
| SaaS dashboard | 520ms | 25ms | 20.8x |
| E-commerce | 870ms | 32ms | 27.2x |
| Enterprise | 1,400ms | 45ms | 31.1x |
Migration Guide
Step 1: Verify Compatibility
Check if your project uses Webpack-specific features that Turbopack doesn't support:
# Common Webpack plugins with Turbopack status
- webpack.DefinePlugin → ✅ Use env vars or Next.js config
- webpack.IgnorePlugin → ✅ Not needed (tree shaking handles this)
- MiniCssExtractPlugin → ✅ Built into Turbopack
- TerserPlugin → ✅ Built into Turbopack
- CopyWebpackPlugin → ⚠️ Use public/ directory instead
- ModuleFederationPlugin → ❌ Not supported yet
- custom Webpack loaders → ⚠️ Partial supportStep 2: Update Configuration
// Before: Webpack customization
module.exports = {
webpack: (config) => {
config.resolve.alias['@components'] = path.resolve(__dirname, 'src/components')
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})
return config
},
}
// After: Turbopack configuration
module.exports = {
turbopack: {
resolveAlias: {
'@components': './src/components',
},
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
}Step 3: Handle Incompatibilities
// If you use features not yet supported by Turbopack:
// Option 1: Conditional bundler selection
// package.json
{
"scripts": {
"dev": "next dev --turbopack",
"dev:legacy": "next dev", // Fallback for Webpack-specific features
"build": "next build" // Production still uses Webpack
}
}
// Option 2: Use environment variable for conditional config
// next.config.js
const isTurbopack = process.env.TURBOPACK === '1'
module.exports = {
...(isTurbopack ? {
turbopack: { resolveAlias: { '@components': './src/components' } }
} : {
webpack: (config) => {
config.resolve.alias['@components'] = path.resolve('src/components')
return config
}
})
}Real-World Use Cases
Use Case 1: Large Enterprise Application
An enterprise application with 5,000+ modules and 200+ pages migrated from Webpack to Turbopack. The development team experienced:
- Cold startup: 120s → 8.5s (14x improvement)
- HMR: 1.4s → 45ms (31x improvement)
- Developer satisfaction: Significantly improved ("changes appear instantly")
- CI/CD: Not affected (production still uses Webpack)
The team estimated that the HMR improvement alone saved each developer 45 minutes per day in waiting time.
Use Case 2: Component Library Development
A component library with 150+ components used Turbopack for rapid iteration during component development. The instant HMR allowed designers and developers to iterate on visual details in real-time, reducing the design-to-implementation cycle from hours to minutes.
Use Case 3: Monorepo with Multiple Apps
A monorepo with 8 Next.js applications and 30+ shared packages used Turbopack for development. The lazy bundling approach meant only the active application's code was processed, reducing startup time from 90s (Webpack processing all apps) to 4s (Turbopack processing only the active app).
Best Practices for Production
-
Use Turbopack for development, Webpack for production: Turbopack is optimized for development workflows. Production builds still benefit from Webpack's mature optimization pipeline.
-
Enable persistent cache: Turbopack's disk cache makes subsequent startups near-instant. Don't disable it unless debugging cache issues.
-
Migrate Webpack config incrementally: Start with
resolve.aliasand simple loader rules. Complex Webpack configurations may need Turbopack-specific alternatives. -
Monitor memory usage: Turbopack uses less memory than Webpack for equivalent workloads, but very large applications may still need memory tuning.
-
Use
optimizePackageImports: This Next.js feature works with Turbopack to reduce the import footprint of large packages like lodash, date-fns, and icon libraries. -
Test with Turbopack in CI: Add a CI job that runs
next dev --turbopackand verifies the development server starts and serves pages correctly. -
Report incompatibilities: If you encounter features that work in Webpack but not Turbopack, report them to the Next.js team. The compatibility gap is closing rapidly.
-
Measure the improvement: Use
timeor built-in profiling to measure cold startup and HMR times before and after enabling Turbopack.
Common Pitfalls and Solutions
| Pitfall | Impact | Solution |
|---|---|---|
| Webpack plugin not supported | Build/dev error | Check Turbopack compatibility; use native alternatives |
| Custom Webpack loader incompatible | Module processing failure | Use Turbopack's rules config or find a Turbopack-compatible loader |
| Missing resolve aliases | Import errors | Migrate resolve.alias to turbopack.resolveAlias |
| CSS preprocessing not working | Styles not applied | Ensure CSS preprocessors (Sass, PostCSS) are configured in next.config.js |
| Source maps different | Debugging difficulty | Turbopack generates source maps; ensure debugger is configured correctly |
| Persistent cache corruption | Startup errors | Delete .next/cache directory and restart |
Performance Optimization
// Optimize Turbopack with package imports
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: [
'lodash', // Import only used functions
'date-fns', // Import only used date functions
'lucide-react', // Import only used icons
'@heroicons/react', // Import only used icons
'rxjs', // Import only used operators
],
},
}
// Instead of importing entire packages:
import _ from 'lodash' // ❌ Imports 70KB+
// Turbopack optimizes to:
import { debounce, throttle } from 'lodash' // âś… Imports only what's usedComparison with Alternatives
| Feature | Turbopack | Vite | Rspack | esbuild |
|---|---|---|---|---|
| Primary Use | Next.js dev | General purpose | Webpack-compatible | Fast builds |
| HMR | 10-45ms | 50-200ms | 30-100ms | N/A |
| Server Components | Native | Plugin-based | Via loaders | N/A |
| Persistent Cache | Yes | No | Yes | No |
| Next.js Integration | Native | N/A | Via config | N/A |
| Maturity | Stable (dev) | Stable | Stable | Stable |
| Production Ready | Not yet | Yes | Yes | Yes |
Advanced Patterns
Custom Turbopack Loaders
// next.config.js
module.exports = {
turbopack: {
rules: {
// SVG as React components
'*.svg': {
loaders: [{
loader: '@svgr/webpack',
options: { icon: true, svgo: true },
}],
as: '*.js',
},
// Custom markdown loader
'*.md': {
loaders: [{
loader: 'remark-loader',
options: { remarkOptions: { plugins: [] } },
}],
as: '*.js',
},
},
},
}Turbopack with Monorepos
// next.config.js for monorepo
const path = require('path')
module.exports = {
turbopack: {
resolveAlias: {
// Reference packages in monorepo
'@shared/ui': path.resolve(__dirname, '../../packages/ui/src'),
'@shared/utils': path.resolve(__dirname, '../../packages/utils/src'),
'@shared/types': path.resolve(__dirname, '../../packages/types/src'),
},
},
}Testing Strategies
// Testing Turbopack configuration
import { createServer } from 'next/dist/server/next'
describe('Turbopack dev server', () => {
let server
beforeAll(async () => {
server = createServer({
dev: true,
turbopack: true,
dir: process.cwd(),
})
await server.prepare()
}, 30000) // Allow 30s for cold start
afterAll(async () => {
await server.close()
})
it('starts within 10 seconds', async () => {
const start = Date.now()
const app = server.getRequestHandler()
const res = await app(new Request('http://localhost:3000'))
const duration = Date.now() - start
expect(duration).toBeLessThan(10000)
expect(res.status).toBe(200)
})
})Turbopack Architecture Deep Dive
Rust-Based Computation Engine
Turbopack's performance advantage comes from its Rust-native implementation. Every operation—parsing JavaScript, resolving imports, transforming syntax, generating bundles—is implemented in Rust, eliminating the overhead of JavaScript's garbage collector and runtime interpretation.
The incremental computation engine tracks dependencies at the function level, not the module level. When you change a single function in a large module, Turbopack invalidates only that function and its dependents, rather than reprocessing the entire module graph. This granular invalidation is the key to sub-50ms HMR times.
// Turbopack's incremental computation model (conceptual)
// When function `calculateTotal` changes:
// 1. Invalidates: calculateTotal
// 2. Invalidates: OrderSummary (uses calculateTotal)
// 3. Invalidates: CheckoutPage (uses OrderSummary)
// 4. Does NOT invalidate: UserAvatar, NavigationBar, ProductCardModule Graph Optimization
Turbopack optimizes the module graph through tree shaking, code splitting, and lazy loading:
// Turbopack automatically code-splits at dynamic import boundaries
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
ssr: false, // Client-only rendering
});
// Route-level code splitting (automatic in App Router)
// app/dashboard/page.tsx is automatically code-split from app/page.tsxPersistent Caching Strategy
Turbopack's persistent cache stores intermediate computation results on disk. On subsequent startups, it loads the cache and only recomputes what changed since the last session:
# Cache location
.next/cache/turbopack/
# Cache contents:
# - Parsed module ASTs
# - Resolved dependency graphs
# - Transformed source code
# - Generated bundle chunks
# Clear cache if corrupted
rm -rf .next/cache/turbopackProduction Build Optimization Strategies
While Turbopack handles development, production builds still use Webpack. Optimize your production builds:
// next.config.js - production optimizations
module.exports = {
// Compression
compress: true,
// Image optimization
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
},
// Bundle analysis
webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
};
}
return config;
},
// Output configuration
output: 'standalone', // For Docker deployments
};Troubleshooting Common Issues
Cache Corruption
If Turbopack behaves unexpectedly, clear the cache:
# Clear all Next.js caches
rm -rf .next
# Clear only Turbopack cache
rm -rf .next/cache/turbopack
# Restart dev server
npm run devMemory Limits
For very large applications, increase Node.js memory:
{
"scripts": {
"dev": "NODE_OPTIONS='--max-old-space-size=8192' next dev --turbopack"
}
}Module Resolution Errors
If imports fail after enabling Turbopack, check your path aliases:
// next.config.js - ensure aliases match tsconfig.json
module.exports = {
turbopack: {
resolveAlias: {
// Must match tsconfig.json paths
'@components': './src/components',
'@lib': './src/lib',
},
},
};CSS and PostCSS Issues
If CSS preprocessing fails, ensure PostCSS and Tailwind are properly configured:
// postcss.config.js - must exist at project root
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};Source Map Debugging
Turbopack generates source maps that work with browser DevTools and VS Code:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node",
"request": "attach",
"port": 9229
}
]
}Future Outlook
Turbopack's roadmap includes production build support (currently in development), incremental compilation for CI/CD, and support for additional frameworks beyond Next.js. The Rust-based tooling trend (SWC, Turbopack, Rspack, OXC) is accelerating, and Turbopack is positioned to become the standard bundler for React applications.
The Next.js team has announced that Turbopack for production is actively being developed, with early benchmarks showing 5-10x faster production builds than Webpack. When production support ships, it will eliminate the need to maintain separate Webpack and Turbopack configurations. The team is also working on Turbopack support for other frameworks, potentially making it a universal Rust-based bundler for the JavaScript ecosystem.
Integration with the React Compiler (formerly React Forget) is another exciting development. The React Compiler can automatically memoize components, and Turbopack's function-level caching makes it the ideal bundler for leveraging compiler optimizations. Together, they promise a future where developers write straightforward React code and the toolchain handles performance optimization automatically without any manual intervention.
Conclusion
Turbopack in Next.js 15 represents the most significant improvement to JavaScript development tooling in a decade. The Rust-based bundler delivers 700x faster incremental builds than Webpack, with HMR times reduced from hundreds of milliseconds to tens of milliseconds. For development workflows, Turbopack transforms the feedback loop from "save, wait, see" to "save, see."
Key takeaways:
- Turbopack is stable for
next dev—enable it immediately for development speed improvements - Production builds still use Webpack—don't use
--turbopackin CI/CD yet - HMR improvements range from 15x to 31x depending on application size
- Persistent cache makes subsequent startups near-instant (0.8s vs 34s for Webpack)
- Migrate Webpack config to Turbopack's
resolveAliasandrulesconfiguration
For the complete Turbopack documentation, visit turbo.build/pack. For migration guidance, consult the Next.js Turbopack guide.