-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Description
Repro
npx degit https://github.com/keycloakify/oidc-spa/examples/react-router-framework oidc-spa-react-router
cd oidc-spa-react-router
cp .env.local.sample .env.local
npm install
npm run dev
# open the printed dev URL and navigate once- This is React Router framework mode configured as SPA (
ssr: false) with onlyoidc-spaset up. - I also recorded a short video showing the behavior (first-visit failure + one-time redirect):
(There is sound)
Screen.Recording.2025-08-22.at.18.27.15.mov
System Info
System:
OS: macOS 15.3
CPU: (12) arm64 Apple M2 Max
Memory: 12.61 GB / 64.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 22.12.0 - ~/.nvm/versions/node/v22.12.0/bin/node
Yarn: 1.22.22 - ~/.nvm/versions/node/v22.12.0/bin/yarn
npm: 11.4.1 - ~/.nvm/versions/node/v22.12.0/bin/npm
pnpm: 10.13.1 - ~/.nvm/versions/node/v22.12.0/bin/pnpm
bun: 1.2.2 - /opt/homebrew/bin/bun
Browsers:
Chrome: 139.0.7258.128
Chrome Canary: 141.0.7369.0
Edge: 139.0.3405.102
Safari: 18.3
npmPackages:
@react-router/dev: 7.8.1 => 7.8.1
@react-router/fs-routes: 7.8.1 => 7.8.1
@react-router/node: 7.8.1 => 7.8.1
@react-router/serve: 7.8.1 => 7.8.1
react-router: 7.8.1 => 7.8.1
vite: 7.1.3 => 7.1.3Used Package Manager
npm
TL;DR
On a fresh npm run dev start, the very first navigation to the app fails with:
Failed to fetch dynamically imported module http://localhost:7173/app/entry.client.tsx
A simple refresh fixes it. Subsequent navigations are fine. Production builds are fine. Also, on the very first attempt to visit a protected route, React Router appears to “hijack” and redirect to / once; this never happens again.
This is confusing for newcomers because their first interaction is an error + odd redirect, even though everything works after a refresh.
Context
I’m building an OIDC client for Single Page Applications called oidc-spa.
I’m currently working on the official React Router framework mode integration example. This behavior does not just affect me, it will potentially impact any team integrating React Router with OIDC client logic in a similar way.
What happens
- Start dev server (
npm run dev). - Open the app for the first time.
- The browser console shows:
The app doesn’t hydrate/mount.
Failed to fetch dynamically imported module http://localhost:7173/app/entry.client.tsx - Refresh the page → everything works.
- Quit and restart the dev server → subsequent first visits work (only the very first fresh start causes it).
- First attempt to navigate to a protected route causes a one-time redirect to
/. Further attempts behave normally.
What I expect
- No failure on the first visit after a fresh dev server start.
- No one-time “mystery” redirect on the first attempt to visit a protected route.
- Same behavior as subsequent navigations / production build.
Why my setup is a bit unusual (but intentional)
I need to run some OIDC bootstrap logic before any other JS runs and sometimes cancel app initialization early (e.g., when the OIDC authorization response lands). So the client entry conditionally lazy-loads the real app:
app/entry.client.tsx
import { oidcEarlyInit } from "oidc-spa/entrypoint";
const { shouldLoadApp } = oidcEarlyInit({
freezeFetch: true,
freezeXMLHttpRequest: true
});
if (shouldLoadApp) {
import("./entry.client.lazy");
}app/entry.client.lazy.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { HydratedRouter } from "react-router/dom";
import { getOidc, OidcProvider } from "./oidc.client";
getOidc().finally(() =>
ReactDOM.hydrateRoot(
document,
<React.StrictMode>
{/*
* NOTE: Do not provide a fallback prop to the <OidcProvider>
* Use the HydrateFallback of ./root.tsx instead
*/}
<OidcProvider>
<HydratedRouter />
</OidcProvider>
</React.StrictMode>
)
);I realize this isn’t the canonical entry pattern, but it’s a common need for SPA OIDC clients to gate app boot.
Observations
- Happens only on the very first page load after the very first dev-server start in a fresh terminal.
- After a single manual refresh, everything settles.
- Production build (
react-router build && react-router serve) works perfectly.
Request
- Can you confirm whether conditional dynamic import at the client entry is supported in framework mode?
- If yes, could this be a dev server preload/resolution bug around the very first request?
- If not supported, what’s the recommended way to gate app boot (i.e., run code before anything else and optionally cancel boot) without tripping the dev server?
Thanks!