Skip to content

Conversation

lukesandberg
Copy link
Contributor

@lukesandberg lukesandberg commented Sep 25, 2025

Make Turbopack the default bundler for Next 🚀

What

Add a --webpack flag so users can select webpack

Change behavior so if neither --turbopack or --webpack is set we default to Turbopack.

If we have defaulted the build to turbopack and we observe that the user has a webpack config in their next config but no turbopack config, then we issue a warning about this being a potential problem, if this happens during next build the warning becomes an error and fails the build. The solution for users is just to explicitly set --webpack or --turbopack

There were a number of subtle issues

  • some users directly set the TURBOPACK environment variable, this PR adds support for that though users should really be passing --turbopack so it will not be documented
  • rspack is enabled via a plugin that sets an environment variable when loading next config, which means it happens way too late!
    • For builds this isn't too bad, we just have to recompute the Bundler after loading the config. For dev the parent process can get out of sync with the child, but this is only really relevant for telemetry and for that we already load the config in the parent so just defer computing isTurboSession.

Most of this is about fixing package.json scripts and CI builds configs.

  • For package.json

    • i added aliases e.g. test-dev == test-dev-webpack but preserved the old names. In the long run we will want to remove the unsuffixed aliases
    • this required some modifications to existing configs to ensure everything was setting the correct env variables
  • For ci, i added a new IS_WEBPACK_TEST variable to a number of tests but preserved all names.

    • Again, in the future it would make sense to rename ci jobs but that is deferred for right now.
    • This also makes it clear that a set of tests scenarios (e.g. ppr, experimental, test-new-tests-*) never run with turbopack. This is not addressed right now but should be in the future.

Why

Today, the default bundler for next is webpack but with turbopack becoming stable it is time to just ship it. Turbopack is already recommended for dev and builds. Create Next App also steers users towards Turbopack. According to telemetry we already have about 50% of all dev sessions and build adoption is growing. So fundamentally why are we even asking users to make a decision?

Closes PACK-5588

Copy link
Contributor Author

lukesandberg commented Sep 25, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@lukesandberg lukesandberg changed the base branch from whole_app_module_graph to graphite-base/84216 September 25, 2025 07:17
@lukesandberg lukesandberg changed the base branch from graphite-base/84216 to canary September 25, 2025 07:17
@ijjk
Copy link
Member

ijjk commented Sep 25, 2025

Tests Passed

@lukesandberg lukesandberg force-pushed the turbopack_default branch 2 times, most recently from 17d13fa to f462ae9 Compare September 25, 2025 23:51
@ijjk ijjk added the tests label Sep 26, 2025
@lukesandberg lukesandberg marked this pull request as ready for review September 26, 2025 07:15
Comment on lines +20 to +62
"test-dev-inner": "cross-env NEXT_TEST_MODE=dev pnpm testheadless",
"test-dev": "pnpm run test-dev-webpack",
"test-dev-webpack": "pnpm run with-webpack pnpm test-dev-inner",
"test-dev-experimental-inner": "pnpm run with-experimental pnpm test-dev-inner",
"test-dev-experimental": "pnpm run test-dev-experimental-webpack",
"test-dev-experimental-webpack": "pnpm run with-webpack pnpm test-dev-experimental-inner",
"test-dev-rspack": "pnpm run with-rspack pnpm run test-dev-inner",
"test-dev-experimental-rspack": "pnpm run with-rspack pnpm run test-dev-experimental-inner",
"test-dev-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm test-dev-inner",
"test-dev-experimental-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm run with-experimental pnpm test-dev-inner",
"test-start-inner": "cross-env NEXT_TEST_MODE=start pnpm testheadless",
"test-start": "pnpm run test-start-webpack",
"test-start-webpack": "pnpm run with-webpack pnpm run test-start-inner",
"test-start-experimental-inner": "pnpm run with-experimental pnpm test-start-inner",
"test-start-experimental": "pnpm run test-start-experimental-webpack",
"test-start-experimental-webpack": "pnpm run with-webpack pnpm run test-start-experimental-inner",
"test-start-rspack": "pnpm run with-rspack pnpm run test-start-inner",
"test-start-experimental-rspack": "pnpm run with-rspack pnpm run test-start-experimental-inner",
"test-start-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm test-start-inner",
"test-start-experimental-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm run with-experimental pnpm test-start-experimental-inner",
"test-deploy-inner": "cross-env NEXT_TEST_MODE=deploy pnpm testheadless",
"test-deploy": "pnpm run test-deploy-webpack",
"test-deploy-webpack": "pnpm run with-webpack pnpm test-deploy-inner",
"test-deploy-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm test-deploy-inner",
"testonly-dev-inner": "cross-env NEXT_TEST_MODE=dev pnpm testonly",
"testonly-dev": "pnpm run testonly-dev-webpack",
"testonly-dev-webpack": "pnpm run with-webpack pnpm run testonly-dev-inner",
"testonly-dev-rspack": "pnpm run with-rspack pnpm run testonly-dev-inner",
"testonly-dev-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm testonly-dev-inner",
"testonly-start-inner": "cross-env NEXT_TEST_MODE=start pnpm testonly",
"testonly-start": "pnpm run testonly-start-webpack",
"testonly-start-webpack": "pnpm run with-webpack pnpm run testonly-start-inner",
"testonly-start-rspack": "pnpm run with-rspack pnpm run testonly-start-inner",
"testonly-start-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testonly-start-inner",
"testonly-deploy-inner": "cross-env NEXT_TEST_MODE=deploy pnpm testonly",
"testonly-deploy": "pnpm run testonly-deploy-webpack",
"testonly-deploy-webpack": "pnpm run with-webpack pnpm run testonly-deploy-inner",
"testonly-deploy-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1 pnpm testonly-deploy-inner",
"test-inner": "pnpm testheadless",
"test": "pnpm test-webpack",
"test-webpack": "pnpm run with-webpack pnpm run test-inner",
"test-rspack": "pnpm run with-rspack pnpm run test-inner",
"test-turbo": "cross-env IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 TURBOPACK_BUILD=1 pnpm test-inner",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thisisfine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image.png

@lukesandberg lukesandberg force-pushed the turbopack_default branch 2 times, most recently from dc752b4 to d462151 Compare September 26, 2025 18:14
Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments:

packages/next/src/server/next.ts (lines 541-543):

The server creation logic uses outdated bundler detection that bypasses the new centralized parseBundlerArgs function, creating inconsistent behavior between CLI and programmatic usage.

View Details
📝 Patch Details
diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts
index 88de1cd32a..d3b68ab21e 100644
--- a/packages/next/src/server/next.ts
+++ b/packages/next/src/server/next.ts
@@ -31,6 +31,7 @@ import {
   RouterServerContextSymbol,
   routerServerGlobal,
 } from './lib/router-utils/router-server-context'
+import { parseBundlerArgs } from '../lib/bundler'
 
 let ServerImpl: typeof NextNodeServer
 
@@ -534,13 +535,16 @@ function createServer(
   options: NextServerOptions & {
     turbo?: boolean
     turbopack?: boolean
+    webpack?: boolean
   }
 ): NextWrapperServer {
-  if (
-    options &&
-    (options.turbo || options.turbopack || process.env.IS_TURBOPACK_TEST)
-  ) {
-    process.env.TURBOPACK = '1'
+  if (options) {
+    // Use centralized bundler argument parsing for consistency with CLI usage
+    parseBundlerArgs({
+      turbo: options.turbo,
+      turbopack: options.turbopack,
+      webpack: options.webpack,
+    })
   }
   // The package is used as a TypeScript plugin.
   if (

Analysis

Inconsistent bundler detection between CLI and programmatic usage

What fails: createServer() in packages/next/src/server/next.ts uses outdated bundler detection logic that bypasses the centralized parseBundlerArgs() function, creating inconsistent behavior between CLI and programmatic usage.

How to reproduce:

// CLI usage (via next-dev.ts) - uses parseBundlerArgs with full feature set
const bundler = parseBundlerArgs({ webpack: true }); // Returns Webpack, no TURBOPACK env set

// Programmatic usage (via createServer) - uses old logic  
const server = createServer({ webpack: true }); // Ignores webpack flag completely

Result: CLI usage supports --webpack flag and conflict detection, while programmatic usage ignores --webpack and has no conflict detection. parseBundlerArgs sets TURBOPACK='auto' for defaults and uses ??= '1' for explicit flags, while createServer always sets TURBOPACK='1'.

Expected: Both CLI and programmatic usage should use the same centralized parseBundlerArgs() function for consistent bundler selection behavior.

if (process.env.IS_RSPACK_TEST) {
setBundlerFlag(
Bundler.Rspack,
`NEXT_TEST_USE_RSPACK=${process.env.NEXT_TEST_USE_RSPACK}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`NEXT_TEST_USE_RSPACK=${process.env.NEXT_TEST_USE_RSPACK}`
`IS_RSPACK_TEST=${process.env.IS_RSPACK_TEST}`

The Rspack test environment variable check uses the wrong variable name in the error message string, causing incorrect debug output.

View Details

Analysis

Incorrect environment variable in Rspack test condition debug message

What fails: parseBundlerArgs() in packages/next/src/lib/bundler.ts checks process.env.IS_RSPACK_TEST but displays process.env.NEXT_TEST_USE_RSPACK value in error messages, causing misleading debug output

How to reproduce:

# Set IS_RSPACK_TEST without NEXT_TEST_USE_RSPACK
IS_RSPACK_TEST=true node -e "console.log('Debug will show: NEXT_TEST_USE_RSPACK=undefined')"

Result: When IS_RSPACK_TEST=true but NEXT_TEST_USE_RSPACK is undefined, debug output shows misleading NEXT_TEST_USE_RSPACK=undefined instead of IS_RSPACK_TEST=true

Expected: Debug message should match the condition variable like other patterns in the same file:

  • IS_TURBOPACK_TESTIS_TURBOPACK_TEST=
  • IS_WEBPACK_TESTIS_WEBPACK_TEST=
  • IS_RSPACK_TEST should → IS_RSPACK_TEST=

@lukesandberg lukesandberg force-pushed the turbopack_default branch 2 times, most recently from 535c612 to 3ebcbc8 Compare September 26, 2025 22:05
Copy link
Contributor Author

good catch by vade, this will go in the next PR. Using the same function is not quite practical but close.

@lukesandberg lukesandberg merged commit 97056e0 into canary Sep 26, 2025
602 of 615 checks passed
Copy link
Contributor Author

Merge activity

@lukesandberg lukesandberg deleted the turbopack_default branch September 26, 2025 23:24
@lukesandberg lukesandberg mentioned this pull request Sep 27, 2025
ijjk added a commit that referenced this pull request Sep 29, 2025
lukesandberg added a commit that referenced this pull request Sep 29, 2025
lukesandberg added a commit that referenced this pull request Sep 29, 2025
mischnic pushed a commit that referenced this pull request Sep 30, 2025
lukesandberg added a commit that referenced this pull request Sep 30, 2025
…84216)"" (#84351)

Reverts #84348  which itself reverted #84216. 

In the 2nd attempt to make --turbopack a default behavior.
* adjust the Generate Pull Request States action to specify `--webpack`
* set the `IS_WEBPACK_TEST` flag in the deployment e2e tests (which are now [passing](https://github.com/vercel/next.js/actions/runs/18113062122))
* make the warning about having a webpack config without a turbopack config more verbose and consistently an error
ijjk added a commit that referenced this pull request Sep 30, 2025
lukesandberg added a commit that referenced this pull request Oct 2, 2025
…rbopack to true (#84216)"""" (#84394)

Reverts #84389

Attempt number 3, #84374 fixed propagation of bundler environment variables to vercel cli operations, to ensure the test configuration is respected. 

[Deployment Tests Run 1](https://github.com/vercel/next.js/actions/runs/18146684793/job/51649631635).  A fair number of failures.

[Deployment Tests Run 2](https://github.com/vercel/next.js/actions/runs/18154875270). After #84395.  These still have a set of failures, but i confirmed that the deployment builds are running `webpack`.  

The tests that are failing are related to 'prefetches' (test/e2e/app-dir/segment-cache/prefetch-runtime/prefetch-runtime.test.ts and test/e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts) and generally the error is a timeout.  Recent deployment runs for canary releases are also failing, but on different tests.


Trying again after #[84419](#84419).  [Deployment Tests Run 3](https://github.com/vercel/next.js/actions/runs/18173375427).  Failures appeared flaky, rerunning failures...
lukesandberg added a commit that referenced this pull request Oct 3, 2025
Make Turbopack the default bundler for custom servers 🚀 

In the same line as #84216 we want to ship turbopack by default, but now for programmatic usecases.

### What?

Have the custom server entrypoint perform the same 'turbopack by default' logic as the main next entrypoints.  This was a little tricky since next itself uses the custom server entrypoints.  So i have adjusted this function to only perform the logic if it is a custom server since otherwise the next process already has the correct environment variables set

Also deprecate the `turbo` property while we are there.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 17, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants