Turbopack in Production With Next.js 16: What's Stable, What Isn't, and How to Migrate From Webpack

Sandor Farkas
Founder & Lead Developer
Expert in software development and legacy code optimization
LinkedInShort answer: Turbopack is the Rust-based bundler built into Next.js, written as the successor to webpack. It has been stable for next dev since Next.js 15 and stable for production builds since Next.js 16 (October 2025), where it became the default for both next dev and next build. For most applications it is production-ready today. The exceptions are apps that depend on webpack plugins, custom webpack() configuration, or a few legacy CSS Modules and Sass features, and those can stay on webpack with next build --webpack while they migrate.
When we first published this post in May 2026, the honest advice was "Turbopack for dev, webpack for production builds, validate in CI." The Next.js 16 releases changed that. As of Next.js 16.3 (August 2026), Turbopack is the default everywhere, its filesystem cache speeds up repeat production builds, and the remaining work is about compatibility with your specific setup rather than about Turbopack's maturity.
A real application is not a clean create-next-app project, though. It has MDX content pipelines, custom loaders for SVG sprites, a Sentry integration that uploads source maps at build time, perhaps a Symfony backend serving JSON through a custom API layer, and a dozen third-party packages that were written with webpack in mind. This post covers what Turbopack covers now, where it still differs from webpack, and how to move an existing app across without a broken deploy.
What Turbopack Is
Turbopack is an incremental bundler for JavaScript and TypeScript, written in Rust by the Next.js team at Vercel. Three design choices explain most of its speed:
- One graph for all environments. Next.js builds for the browser, the Node.js server and Server Components at the same time. Turbopack keeps all of them in a single module graph instead of running separate compilers and stitching the output together.
- Incremental computation. Work is cached down to the function level and reused. Since Next.js 16.1 this cache persists to disk between runs, and since 16.3 the disk cache is on by default for both
next devandnext build. - Lazy bundling in development. The dev server compiles only the routes you actually request. On a large app, routes you never open during a session are never compiled.
It uses SWC for JavaScript and TypeScript transforms and Lightning CSS for CSS. It does not type-check your code: next build still runs TypeScript separately, and since Next.js 16.3 it can use the much faster TypeScript 7 compiler for that step.
Is Turbopack Production-Ready?
For the default case, yes. The version history is short:
| Next.js version | Turbopack status |
|---|---|
| 15.0 (October 2024) | Stable for next dev |
| 15.3 | Experimental support for next build |
| 15.5 | next build --turbopack in beta |
| 16.0 (October 2025) | Stable and default for next dev and next build |
| 16.1 (December 2025) | Filesystem cache for next dev stable |
| 16.2 (March 2026) | Tree shaking of dynamic imports, SRI support, postcss.config.ts, 200+ fixes |
| 16.3 (August 2026) | Disk cache on by default for next build, up to 90% less dev memory, import.meta.glob |
When Vercel made it the default in Next.js 16, they reported that more than half of development sessions and a fifth of production builds on Next.js 15.3 and later were already running on Turbopack. Their headline numbers for the switch were 2 to 5 times faster production builds and up to 10 times faster Fast Refresh. For 16.3 they published cached next build compile times between 1.4 and 5.5 times faster than cold builds on their own sites. Treat these as vendor numbers from Vercel's own projects and measure your own app before you quote them to anyone.
"Production-ready" does not mean "identical to webpack." The rest of this post is about the differences that matter.
What Happens When You Upgrade to Next.js 16
The upgrade itself is where most teams first meet Turbopack in production. Three things to know:
next build fails if you have a webpack() function in next.config. Next.js 16 refuses to silently ignore custom webpack configuration. You have three options: migrate the configuration to the turbopack key, run next build --turbopack to build with Turbopack and ignore the webpack config (useful to test whether you still need it), or keep webpack with next build --webpack and next dev --webpack.
The turbopack config moved. It is now a top-level key in next.config.ts, no longer under experimental. Webpack loaders are registered under turbopack.rules:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
}
export default nextConfig
Babel is picked up automatically. If the project has a Babel config file, Turbopack now applies it instead of exiting with an error. That keeps old setups building, but Babel is slower than SWC, so check whether you still need it. The main remaining reason is the React Compiler, which still runs through Babel unless you opt into the experimental Rust port in 16.3.
Known Gaps With Webpack
The Next.js documentation lists the behaviour differences explicitly. The ones that matter most for typical production apps:
Webpack plugins are not supported. Loaders are, plugins are not. Anything that hooks into webpack's compiler lifecycle (asset manifest generators, custom chunk naming, some i18n and icon-font plugins) needs a Turbopack-compatible replacement or has to stay on webpack. This is the most common real blocker.
Loaders run in a compatibility layer. Simple loaders such as @svgr/webpack, raw-loader or yaml-loader work through turbopack.rules. Loaders that depend on the full webpack compiler API, emit extra files, or manipulate the module graph are the ones that break. Since 16.2 you can also configure a loader for a single import with import attributes (with { turbopackLoader: 'raw-loader' }), which helps when only one file needs special treatment.
MDX plugins must be serializable. With @next/mdx, remark and rehype plugins have to be passed by name as strings ('remark-gfm', or ['remark-toc', { heading: 'Contents' }]), because JavaScript functions cannot be passed to the Rust side. A plugin whose options include a function cannot be used with Turbopack yet. This is the usual cause of the "MDX works on webpack, fails on Turbopack" reports.
CSS Modules ordering follows import order. Turbopack orders CSS Modules by JavaScript import order. Webpack sometimes ignores that order when it decides a module is side-effect-free. Apps that accidentally depended on webpack's ordering can see subtle style differences. The fix is to make the dependency explicit with an @import or to stop two rules from targeting the same property.
Legacy CSS Modules and Sass features. Standalone :local/:global pseudo-classes, @value, :import/:export ICSS rules and custom sassOptions.functions are not supported. The old ~ prefix for Sass imports from node_modules is not supported either, but a resolveAlias entry ('~*': '*') maps it.
CSS precision. Lightning CSS writes 5 decimal places where webpack's pipeline wrote 10. It rarely matters, but it can shift computed line-height values by a sub-pixel amount in visual regression tests.
Linked packages outside the project root. Turbopack does not resolve files outside the project root by default, which affects npm link setups and some monorepo layouts. Set turbopack.root to the common parent directory.
Unsupported and unplanned. Yarn PnP, experimental.urlImports and experimental.esmExternals are not planned for Turbopack. Platforms without native bindings (FreeBSD, OpenBSD) fall back to WASM bindings that do not support Turbopack at all.
Sentry, Source Maps and Tree Shaking
Three production concerns deserve their own check before you switch primary production builds.
Sentry. Sentry reworked its Next.js SDK so that it no longer depends on a bundler plugin. Source map upload with Turbopack builds requires @sentry/nextjs 10.13.0 or later and Next.js 15.4.1 or later, and the upload happens after the build completes. If you pinned an older SDK version, upgrade it before you switch. Then trigger a test error in a staging deploy and confirm the stack trace resolves to your original source.
Source maps in general. Turbopack emits browser source maps in production when productionBrowserSourceMaps is enabled, the same switch as with webpack. If you ship to Datadog, Bugsnag or a self-hosted error tracker, validate symbolication the same way you would for Sentry.
Tree shaking and side effects. Turbopack infers side-effect-free modules and removes unused exports in production builds. Packages that register globals or patch prototypes on import, and that also declare "sideEffects": false in their package.json, can be removed incorrectly. That is a bug in the package metadata, and webpack can hit it too, but a bundler switch is when it tends to surface. Keep an explicit side-effect import (import 'some-analytics-sdk/register') for anything that must run on load, and compare bundle output before and after.
A Migration Path for Existing Apps
The three-phase plan from the first version of this post still works. The phases are just shorter now that the default has flipped.
Phase one: build with Turbopack in CI, deploy with webpack. If you are still on --webpack, add a CI job that runs next build --turbopack on every pull request without gating the deploy on it. Treat failures as a to-do list: each one is a plugin to replace, a loader to move into turbopack.rules, or an MDX plugin to pass by name. For a typical app the list is short, and each item is a contained fix.
Phase two: compare the output. Once the Turbopack build is green, compare it with the webpack build: First Load JS per route in the build output, the rendered CSS on your key pages (visual regression tests catch the ordering issue above), and error symbolication in a staging deploy. Differences are not automatically bugs, but you should be able to explain each one.
Phase three: switch production, keep the escape hatch. Deploy Turbopack builds to staging, then production. Keep the --webpack scripts in package.json for one or two release cycles so a rollback is a one-line change. After that, remove the webpack configuration entirely, because a config that no build uses will drift out of date.
If CI does not preserve .next/cache between runs, turn off turbopackFileSystemCacheForBuild or, better, cache the directory. The build cache is where much of the 16.3 speedup comes from.
For Symfony-backed Next.js applications, the Symfony side rarely matters for the bundler choice. Build-time integrations with a Symfony API are data fetching, not loaders or plugins, so they carry over unchanged. The friction is almost always in the asset pipeline configuration.
When to Stay on Webpack for Now
Staying on webpack is still a valid choice in a few cases: you depend on a webpack plugin with no Turbopack equivalent, you need sassOptions.functions or Yarn PnP, or you build on a platform without Turbopack bindings. Webpack remains available with --webpack in Next.js 16. Document the reason, check it again with each minor release, and make sure new code does not add more webpack-only dependencies in the meantime.
Worth the Effort
For most Next.js applications, the Turbopack question has changed from "is it ready?" to "what in our setup still assumes webpack?" The answer is usually a short list: a plugin or two, a loader configuration, an MDX setup, and a Sentry version bump. Working through that list gets you faster builds and a much faster dev loop, and it takes you off a code path the framework no longer treats as the default.
If your Next.js codebase has accumulated significant custom build configuration and you want an outside assessment of the Turbopack migration before you start, Wolf-Tech helps engineering teams plan and carry out exactly this kind of technical transition. We have worked through build pipeline migrations on production Next.js applications with complex dependency graphs, and a configuration review usually surfaces the blockers early. Reach out at hello@wolf-tech.io or visit wolf-tech.io to talk through your setup.
