Skip to content

fix: migrate react-router to v8 and patch postcss advisory#113

Merged
balebbae merged 1 commit into
mainfrom
caleb/router-v8
Jul 25, 2026
Merged

fix: migrate react-router to v8 and patch postcss advisory#113
balebbae merged 1 commit into
mainfrom
caleb/router-v8

Conversation

@balebbae

Copy link
Copy Markdown
Collaborator

Why

CI's audit gate (npm audit --audit-level=high --omit=dev) was failing on two advisories:

  • GHSA-qwww-vcr4-c8h2 (high, CSRF) — affects react-router >=7.12.0 <8.3.0. Only fixed in v8.
  • GHSA-r28c-9q8g-f849 (high, path traversal in postcss source-map auto-loading) — affects postcss <=8.5.17.

Changes

package.json

  • react-router-dom@^7.12.0react-router@^8.3.0. The react-router-dom package no longer exists in v8; it was only ever a re-export shim to smooth the v6→v7 upgrade.
  • react / react-dom ^19.2.0^19.2.7 — required, v8's peer range is >=19.2.7 and the tree was resolving 19.2.4.
  • overrides.postcss ^8.4.38^8.5.18. The override already existed but was loose enough to keep resolving 8.5.15; now resolves 8.5.23.

31 source files — import specifier rewrite only, no API changes. Every export this app uses (useNavigate, Navigate, Link, NavLink, Outlet, useLocation, useSearchParams, createBrowserRouter, RouterProvider, useRouteError, isRouteErrorResponse) is exported from react-router root in v8.

The one non-mechanical change is src/main.tsx, which imports RouterProvider from react-router/dom, not the root. Verified against the published v8 package rather than the docs: react-router/dom exports only RouterProvider, HydratedRouter, and RSC APIs, and its RouterProvider is the root one pre-wired with ReactDOM.flushSync — exactly what v7's react-router-dom export was. This preserves existing behavior.

No future.* flags, loaders, actions, meta(), or useMatches() in this app, so none of v8's other breaking changes apply. Node 22 (CI) and 22.23.1 (Docker) both satisfy v8's >=22.22.0 engine requirement.

Verification

Run against a clean npm ci:

Check Result
npm run format:check pass
npm run lint 0 errors (1 pre-existing warning, react-hook-form's watch())
npm run build pass
npm audit --audit-level=high --omit=dev exit 0

Neither advisory appears in the full audit. Remaining findings are dev-only and pre-existing on main (brace-expansion, fast-uri, js-yaml via the eslint toolchain — excluded by --omit=dev; esbuild is low, below threshold).

Runtime, driven headlessly against the dev server — 15/15:

  • Login page renders; all guarded routes (/app, /app/status, /app/apply, /admin, /admin/all-applicants, /admin/sa/reviews, /admin/sa/walk-in-queue) redirect to /
  • Public auth routes (/auth/verify, /auth/callback/google) still match and don't fall through
  • * catch-all renders ErrorPage; navigate("/") stays client-side; history back works
  • No router-related console or page errors

Navigation-state semantics were also exercised directly against the installed v8 router core (9/9) — navigate(path, { state }) delivery, and navigate(path, { replace: true, state: null }) clearing state while emitting a new location key so a consuming effect re-runs once and terminates. That matters for the justSubmitted handling on #106's status page, which this PR doesn't contain.

Not covered

Authenticated surfaces were not exercised in a browser — no session was available. Worth a signed-in click-through on the admin sidebar links, the grading pages' useSearchParams reads, and especially NavUser's view-switch, which does requestAnimationFrame(() => navigate(target)) to dodge a Radix pointer-events bug and is timing-sensitive against router internals.

🤖 Generated with Claude Code

@balebbae
balebbae merged commit 84c12fb into main Jul 25, 2026
3 checks 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.

1 participant