Skip to content

Migrate react-router-dom v7 to react-router v8 - #101

Merged
AlvanChow merged 1 commit into
mainfrom
claude/llm-security-audit-cvz9uy
Jul 29, 2026
Merged

Migrate react-router-dom v7 to react-router v8#101
AlvanChow merged 1 commit into
mainfrom
claude/llm-security-audit-cvz9uy

Conversation

@AlvanChow

Copy link
Copy Markdown
Owner

Unblocks the deploy pipeline. npm audit --omit=dev --audit-level=high has been failing on react-router GHSA-qwww-vcr4-c8h2 in both ci.yml and deploy.yml — and in deploy.yml that audit runs at step 4, before build and wrangler deploy. So it was not merely a red check: production releases were blocked outright, including the security work merged in #100.

The advisory was never exploitable here

It affects only the unstable RSC APIs. The advisory says so directly, and its predecessor (GHSA-h5cw-625j-3rxh) is explicit that declarative mode is unaffected. The fix, remix-run/react-router#15311, changes exactly one source file — packages/react-router/lib/rsc/server.rsc.ts — reachable only through the react-server conditional export that a browser SPA bundle never resolves. This app is declarative-mode only: no SSR, no RSC, no data router, no loaders or actions.

So this is CI hygiene, not an incident response. It still has to happen, because the gate blocks deploys.

Why a migration rather than a version bump

There is no patched 7.x. The advisory range covers 7.12.0 through 8.2.0, react-router-dom ends at 7.18.1 with no v8 line at all, and the only patched version is react-router@8.3.0. Upgrading therefore means moving off react-router-dom, whose v6-compat re-export shim is removed in v8.

Changes

  • Replace react-router-dom with react-router@^8.3.0. The import specifier moves in 34 files: 32 component imports, the vi.mock in Dashboard.test.jsx, and the manualChunks entry in vite.config.js. Nothing else — every one is a one-line change.
  • All nine APIs in use are unchanged in v8 and verified present in its root export: BrowserRouter, Routes, Route, Navigate, Link, MemoryRouter, useNavigate, useLocation, useParams. Only RouterProvider/HydratedRouter moved to react-router/dom, and neither is used here.
  • Raise react/react-dom floors to ^19.2.7 to match v8's declared peer range. The previous ^19.2.0 permitted an under-minimum resolution even though npm happens to resolve 19.2.8 today.
  • Raise engines.node to >=22.22.0 to match v8. CI and deploy both run Node 24.

Verification

npm run check passes end to end for the first time — lint, 361 tests across 42 files, share-catalog verification, build, and npm audit --omit=dev --audit-level=high reporting 0 vulnerabilities.

Unit tests alone are thin cover for a routing major, so I also drove the real production build in Chromium behind an SPA-fallback server matching the Worker's not_found_handling, exercising every router API in use:

✓ / renders Landing                            (eager route)
✓ /how-it-works renders its own content        found "The Matching Engine"
✓ /privacy renders its own content             found "Last updated July 27, 2026"
✓ /quiz/mbti renders its own content           found "Which of the 16 types are you?"
✓ /quiz/pokemon resolves useParams             rendered the pokemon vector quiz
✓ /crew redirects to /circle (Navigate)        landed on /circle
✓ /not-a-real-page renders NotFound            NotFound body rendered
✓ useNavigate moves route imperatively         navigate() -> /quiz/mbti
✓ <Link> navigates client-side                 -> /privacy, no reload: true

The lazy-route checks assert on page-unique strings rather than the shared header and footer — an earlier pass matched nav text and gave three false positives before I tightened them.

Deploy note

Nothing in this PR needs a migration or a secret. Once it lands, deploy.yml clears the audit gate and will ship both this and the security work from #100, at which point the SHARE_PROXY_SECRET setup in that PR's description becomes worth doing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PmuxLEfqmMeqBg8gpuXNHa


Generated by Claude Code

Clears GHSA-qwww-vcr4-c8h2, which was failing `npm audit --omit=dev
--audit-level=high` in both ci.yml and deploy.yml. The deploy workflow runs
that audit before build and `wrangler deploy`, so the gate was blocking
production releases outright, not just showing a red check.

The advisory was never exploitable here — it affects only the unstable RSC
APIs, and the fix (remix-run/react-router#15311) touches a single file,
packages/react-router/lib/rsc/server.rsc.ts, reachable only through the
`react-server` conditional export that a browser SPA bundle never resolves.
This app is declarative-mode only. But there is no patched 7.x: the advisory
range covers 7.12.0 through 8.2.0, `react-router-dom` ends at 7.18.1 with no
v8 line, and the sole patched version is react-router@8.3.0.

- Replace the react-router-dom dependency with react-router@^8.3.0. In v8 the
  v6-compat re-export shim is gone, so the import specifier moves in 34 files
  (32 imports plus the vi.mock in Dashboard.test.jsx and the manualChunks
  entry in vite.config.js). All nine APIs this app uses — BrowserRouter,
  Routes, Route, Navigate, Link, MemoryRouter, useNavigate, useLocation,
  useParams — are unchanged in v8 and verified present in the v8 root export.
  Only RouterProvider/HydratedRouter moved to react-router/dom; neither is
  used here.
- Raise react and react-dom floors to ^19.2.7 to match v8's declared peer
  range. The old ^19.2.0 permitted an under-minimum resolution even though
  npm happens to resolve 19.2.8 today.
- Raise engines.node to >=22.22.0 to match v8. CI and deploy both run Node 24.

Verified: npm run check passes end to end for the first time, including
`npm audit --omit=dev --audit-level=high` reporting 0 vulnerabilities.
361 tests across 42 files pass. Also driven in Chromium against the real
production build with SPA fallback, exercising every router API in use:
eager and lazy routes render their own content, useParams resolves through
the quiz registry, Navigate redirects /crew to /circle, the catch-all
renders NotFound, useNavigate moves route imperatively, and Link navigates
client-side without a document reload.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PmuxLEfqmMeqBg8gpuXNHa
@AlvanChow
AlvanChow merged commit b2626df into main Jul 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants