-
-
Notifications
You must be signed in to change notification settings - Fork 10.7k
Description
Reproduction
- Create a new React Router 7.6.0 project with Vite
- Start development server (
npm run dev
) - Open the app in Safari desktop
- Make changes to a route file (e.g.,
app/routes/home.tsx
) - Observe HMR working correctly
- Refresh the page in Safari
- Notice the page loads the old cached version with hydration errors
Environment: Any React Router 7.1.0+ project with Vite dev server
System Info
System:
OS: macOS (darwin 24.2.0)
Binaries:
Node: 22.14.0
npm: 10.7.0
Browsers:
Safari: Affected
Chrome: Works fine
Firefox: Works fine
npmPackages:
react-router: 7.6.0
@react-router/dev: 7.6.0
@react-router/node: 7.6.0
vite: 6.0.3
Used Package Manager
npm
Expected Behavior
After making changes to route files and refreshing the page in Safari:
- Page should load the latest version of the code
- No hydration errors should occur
- HMR should continue working normally
Actual Behavior
After making changes to route files and refreshing the page in Safari:
- Page loads an old cached version of the route
- Hydration errors occur due to version mismatch
- Development workflow is broken for Safari users
Issue Flow:
- Load app → ✅ Works
- Make changes → ✅ HMR works
- Make more changes → ✅ HMR continues working
- Refresh page → ❌ BROKEN - Loads old cached version with hydration errors
Browser Scope: Safari desktop only (Chrome/Firefox unaffected)
Affected Versions: 7.1.0 through 7.6.0+
Root Cause & Fix
React Router 7.1.0+ removed query string concatenations from route imports, breaking Safari's module cache invalidation. Safari requires query parameters to distinguish between different versions of route files.
Fix: Restore ?__react-router-build-client-route
query strings in packages/react-router-dev/vite.ts
at lines ~2920, ~4022, and ~3950.
Required Fixes
File: packages/react-router-dev/vite.ts
(or compiled dist/vite.js
)
Fix 1 - Restore routeModulePath query string (~line 2920):
let routeModulePath = combineURLs(
ctx.publicPath,
- `${resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`
+ `${resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}?__react-router-build-client-route`
);
Fix 2 - Restore moduleUrl query string (~line 4022):
let moduleUrl = combineURLs(
ctx.publicPath,
- `${resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`
+ `${resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}?__react-router-build-client-route`
);
Fix 3 - Fix Fast Refresh compatibility (~line 3950):
function addRefreshWrapper(reactRouterConfig, code, id) {
- let route = getRoute(reactRouterConfig, id);
+ // Strip query string for route lookup (e.g., remove ?__react-router-build-client-route)
+ let idForRouteMatch = id.split('?')[0];
+ let route = getRoute(reactRouterConfig, idForRouteMatch);
Validation
✅ Tested Fix: Applied manually to node_modules/@react-router/dev/dist/vite.js
in React Router 7.6.0
✅ Result: Safari HMR now works correctly after page refresh
✅ No Regression: Chrome/Firefox continue working as expected