跪拜 Guibai
← All articles
Preact · Frontend · TypeScript

React Router v8 Upgrade Cost Two Days — Here’s What Actually Broke

By Flynt ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

React Router downloads exceed 50 million per week; a "boring" release that quietly mandates middleware, ESM-only, and Node 22 will break CI pipelines and silently corrupt pre-rendered pages for teams that treat it as a routine dependency bump.

Summary

A v7-to-v8 upgrade that was billed as a boring, flag-compatible release turned into a two-day effort across import paths, middleware, CI infrastructure, and a pre-rendering bug. The package rename from react-router-dom to react-router is mechanical but breaks third-party libraries still depending on the old name. Middleware is now default behavior, forcing auth logic out of loaders and into a middleware pipeline — otherwise loaders and middleware fight each other, causing flash redirects. Node 22.22.0+, React 19.2.7+, and Vite 7+ are hard requirements, so CI runners and Docker images stuck on Node 20 will fail. ESM-only enforcement breaks older webpack/Jest setups, and a pre-rendering serialization bug was only fixed in v8.3.1. v6 and Remix v2 are now EOL with no further security patches.

Takeaways
react-router-dom is deleted in v8; all imports must switch to react-router, with DOM-specific APIs like ScrollRestoration coming from react-router/dom.
Third-party libraries that still peer-depend on react-router-dom will throw Module not found errors and need a resolve alias as a stopgap.
Middleware is now default behavior — the v8_middleware future flag is removed — so auth logic must move from loaders into a middleware function to avoid double-processing and flash redirects.
v8 requires Node 22.22.0+, React 19.2.7+, and Vite 7+; CI workflows and Docker images pinned to Node 20 will fail.
Node 22 changes --experimental-webstorage defaults, which can break test utilities that mock localStorage or sessionStorage; a NODE_OPTIONS workaround may be needed.
The v8_passThroughRequests flag is removed and became default, but a pre-rendering loader-data serialization bug existed until v8.3.1.
v8 is ESM-only, forcing tsconfig target/lib to ES2022 and breaking CommonJS require() and jest.config.js setups; webpack 4 + Jest 27 combinations are especially affected.
splitRouteModules is now a top-level config and enabled by default, which can alter build output size and code-splitting behavior.
The loader data parameter is renamed to loaderData, and the meta API must follow suit; custom hooks referencing the old data field need global find-and-replace.
React Router v6 and Remix v2 are officially EOL and will receive no further security patches, making the upgrade mandatory for those versions.
Conclusions

Calling a release "boring" for a package with 50M weekly downloads sets a false expectation: the changelog lists flag-compatible changes, but the real cost is architectural — moving auth from loaders to middleware is a design decision, not a find-and-replace.

The middleware-vs-loader conflict is a silent failure mode: the project runs, but pages flash and redirect unexpectedly because two auth layers execute in the wrong order with no clear error.

Infrastructure drift between local dev (Node 22) and CI/Docker (Node 20) is a common hidden tax on framework upgrades; the code works locally, but the pipeline reveals the real floor.

ESM-only is a hard cut that disproportionately punishes projects still on webpack 4 and older Jest, effectively bundling a build-tool migration into a router version bump.

The pre-rendering bug that shipped in v8.3.0 and was fixed in v8.3.1 shows that even "default behavior" carry-overs from removed flags can regress — waiting one patch version matters.

TanStack Router is gaining converts specifically because React Router's breaking changes are perceived as recurring; the switching cost is high for loader/action-heavy projects, but the frustration is real.

Concepts & terms
React Router middleware (v8)
A pipeline function that runs before route loaders, receiving the request and a context object. It can authenticate, redirect, or attach data to context for downstream loaders, replacing the v7 pattern of putting auth inside loaders.
ESM-only
A package distribution that ships only ES modules (import/export syntax) and drops CommonJS (require/module.exports) support, forcing consumers to use ES module-compatible bundlers and Node configurations.
future flags
React Router's opt-in configuration flags in v7 that let projects adopt upcoming v8 breaking changes incrementally before the major release, intended to make the final upgrade a no-op.
splitRouteModules
A v8 top-level config (default on) that splits route modules into separate chunks for code-splitting; previously controlled by a future flag in v7.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗