From 4a85e563da81e0305b59306398f3cd988b83afc0 Mon Sep 17 00:00:00 2001 From: Geoff Hubbard Date: Mon, 4 Aug 2025 12:08:48 +0200 Subject: [PATCH 1/5] Fix webpack build failures with React 18 This commit addresses webpack module resolution errors that occur when bundling React Router's RSC features in React 18 environments, where React.use does not exist. **Problem:** Webpack's static analysis phase occurs before any runtime code executes. When webpack encounters `React.use(getPayload())` in the source code, it attempts to validate that the `use` export exists in the React module during build time. In React 18, this export doesn't exist, causing the build error: ``` export 'use' (imported as 'React5') was not found in 'react' (possible exports: Children, Component, Fragment, Profiler, PureComponent, StrictMode, Suspense, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, act, cloneElement, createContext, createElement, createFactory, createRef, forwardRef, isValidElement, lazy, memo, startTransition, unstable_act, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore, useTransition, version) ``` **Solution:** Implemented `react18compatibleUse` function that: Moves the property resolution from build-time to runtime, bypassing webpack's static validation while maintaining the same functionality. I tried many other alternate solutions but they all failed due to them being runtime solutions, but webpack's static analysis happens _before_ runtime. This solution: 1. **Hides property access from static analysis** using dynamic property lookup with a string variable instead of direct `React.use` access 2. **Provides React 18/19 compatibility** by detecting if React.use exists at runtime and falling back to an informative error function 3. **Maintains identical API** to React.use, enabling seamless migration when React 18 support is eventually dropped 4. **Preserves type safety** with proper generic typing matching React 19's Usable interface --- packages/react-router/lib/rsc/server.ssr.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/react-router/lib/rsc/server.ssr.tsx b/packages/react-router/lib/rsc/server.ssr.tsx index 7d5f43f703..0d788fe5f7 100644 --- a/packages/react-router/lib/rsc/server.ssr.tsx +++ b/packages/react-router/lib/rsc/server.ssr.tsx @@ -8,6 +8,21 @@ import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries"; import { shouldHydrateRouteLoader } from "../dom/ssr/routes"; import type { RSCPayload } from "./server.rsc"; +const react18compatibleUse = (() => { + type Usable = Promise | React.Context; + + const errorUse = function (_: Usable): T { + throw new Error("React Router v7 requires React 19+ for RSC features."); + }; + + const functionName = "use"; + const realUse = (React as any)[functionName]; + + return function (promise: Usable): T { + return (realUse || errorUse)(promise); + }; +})(); + export type SSRCreateFromReadableStreamFunction = ( body: ReadableStream, ) => Promise; @@ -196,8 +211,8 @@ export interface RSCStaticRouterProps { * @returns A React component that renders the {@link unstable_RSCPayload} as HTML. */ export function RSCStaticRouter({ getPayload }: RSCStaticRouterProps) { - // @ts-expect-error - need to update the React types - const payload = React.use(getPayload()) as RSCPayload; + // Can be replaced with React.use when v18 compatibility is no longer required. + const payload = react18compatibleUse(getPayload()) as RSCPayload; if (payload.type === "redirect") { throw new Response(null, { From be426e224627c3f1c755d979929dc8bcec8b6ffe Mon Sep 17 00:00:00 2001 From: Geoff Hubbard Date: Mon, 4 Aug 2025 12:57:27 +0200 Subject: [PATCH 2/5] Add my handle to contributors.yml --- contributors.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/contributors.yml b/contributors.yml index 1866d7c80a..41d4ad775e 100644 --- a/contributors.yml +++ b/contributors.yml @@ -125,6 +125,7 @@ - gatzjames - gavriguy - Geist5000 +- GeoffKarnov - gesposito - gianlucca - gijo-varghese From f914786ecb01cb1314ce8ef04983c273ff99a180 Mon Sep 17 00:00:00 2001 From: Geoff Hubbard Date: Mon, 4 Aug 2025 13:01:37 +0200 Subject: [PATCH 3/5] Added changeset --- .changeset/rotten-foxes-deliver.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rotten-foxes-deliver.md diff --git a/.changeset/rotten-foxes-deliver.md b/.changeset/rotten-foxes-deliver.md new file mode 100644 index 0000000000..6fbe12f937 --- /dev/null +++ b/.changeset/rotten-foxes-deliver.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +Prevent webpack error with React 18 when defining code that uses React.use From 095010b89cf4654b8357a179890c463b90afc3b7 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 4 Aug 2025 14:15:46 -0400 Subject: [PATCH 4/5] Apply suggestions from code review --- packages/react-router/lib/rsc/server.ssr.tsx | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/react-router/lib/rsc/server.ssr.tsx b/packages/react-router/lib/rsc/server.ssr.tsx index 0d788fe5f7..d8ca8fc023 100644 --- a/packages/react-router/lib/rsc/server.ssr.tsx +++ b/packages/react-router/lib/rsc/server.ssr.tsx @@ -8,20 +8,18 @@ import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries"; import { shouldHydrateRouteLoader } from "../dom/ssr/routes"; import type { RSCPayload } from "./server.rsc"; -const react18compatibleUse = (() => { - type Usable = Promise | React.Context; +// Safe version of React.use() that will not cause compilation errors against +// React 18 and will result in a runtime error if used (you can't use RSC against +// React 18). +const REACT_USE = "use"; +const useImpl = (React as any)[REACT_USE]; - const errorUse = function (_: Usable): T { - throw new Error("React Router v7 requires React 19+ for RSC features."); - }; - - const functionName = "use"; - const realUse = (React as any)[functionName]; - - return function (promise: Usable): T { - return (realUse || errorUse)(promise); - }; -})(); +function useSafe(promise: Promise | React.Context): T { + if (useImpl) { + return useImpl(promise); + } + throw new Error("React Router v7 requires React 19+ for RSC features."); +} export type SSRCreateFromReadableStreamFunction = ( body: ReadableStream, @@ -212,7 +210,7 @@ export interface RSCStaticRouterProps { */ export function RSCStaticRouter({ getPayload }: RSCStaticRouterProps) { // Can be replaced with React.use when v18 compatibility is no longer required. - const payload = react18compatibleUse(getPayload()) as RSCPayload; + const payload = useSafe(getPayload()); if (payload.type === "redirect") { throw new Response(null, { From ee71d02690cb3b46a628ff2b7717d06646acafbf Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 4 Aug 2025 14:17:31 -0400 Subject: [PATCH 5/5] Update .changeset/rotten-foxes-deliver.md --- .changeset/rotten-foxes-deliver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rotten-foxes-deliver.md b/.changeset/rotten-foxes-deliver.md index 6fbe12f937..24bad7cfda 100644 --- a/.changeset/rotten-foxes-deliver.md +++ b/.changeset/rotten-foxes-deliver.md @@ -2,4 +2,4 @@ "react-router": patch --- -Prevent webpack error with React 18 when defining code that uses React.use +Adjust internal RSC usage of `React.use` to avoid Webpack compilation errors when using React 18