feat(core): upgrade React Router v5 → v8 (and history v5)#12190
Draft
slorber wants to merge 2 commits into
Draft
feat(core): upgrade React Router v5 → v8 (and history v5)#12190slorber wants to merge 2 commits into
slorber wants to merge 2 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Size Change: +154 kB (+1.28%) Total Size: 12.2 MB 📦 View Changed
ℹ️ View Unchanged
|
⚡️ Lighthouse report for the deploy preview of this PR
|
✅ [V2]
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ [V2]
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Size Change: +169 kB (+1.36%) Total Size: 12.6 MB 📦 View Changed
ℹ️ View Unchanged
|
POC migrating Docusaurus from react-router v5 to react-router v8, and from
the bundled history v4 to the standalone history package v5 (latest versions).
The bulk of the work is the v5 → v6 jump (react-router-config removed,
controlled `<Route location>` removed, `NavLink` `isActive`/`activeClassName`
removed, no more exposed `history` object / `useHistory`). v6 → v7 → v8 are
mostly package consolidation (`react-router-dom` merged into `react-router`,
then dropped) plus ESM-only / Node 22+ / React 19+ requirements.
Key changes:
- Reimplement `react-router-config`'s `renderRoutes` as a small `<Switch>`-like
component (string-based, recursive, first-match-wins route config), and keep
a thin v5-compatible `matchPath`/`matchRoutes` built on top of React Router's
own `matchPath` (so we don't hand-roll path matching). React Router v8 is
ESM-only, so the CommonJS broken-links checker loads `matchPath` via a dynamic
`import('react-router')`.
- Drive react-router with a `history`-package instance through the low-level
`<Router navigator={history}>` (replaces `BrowserRouter`/`HashRouter`/
`StaticRouter` and the removed `unstable_HistoryRouter`); `@docusaurus/router`
`useHistory()` returns it so history blocking/listening/querystring helpers
keep working.
- Replace the controlled `<Route location>` trick used by `PendingNavigation`
with a location-override React context.
- Reimplement Docusaurus `<Link>`/NavLink active behavior
(`isActive`/`activeClassName`/`activeStyle`) on top of react-router's
`<Link>` + `useLocation`/`matchPath`.
- `Redirect` → `Navigate` compatibility shim; adapt `historyUtils` to
history v5's new `block()` transition API.
Not done (POC scope): full type-safety polish.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111b179 to
eb15b22
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warning
Draft / proof-of-concept. The goal of this PR is to prove that Docusaurus can build on the latest React Router (v8) and
history(v5). It deliberately skips full type-safety polish for now — see the unchecked items below.Pre-flight checklist
brokenLinkstest suite was updated for the new matcher.)Motivation
Docusaurus is still on React Router v5 (and
historyv4), both unmaintained for years. This PR is a proof-of-concept upgrade all the way to the latest releases:react-router^5.3.4^8.0.1react-router-dom^5.3.4react-router-config^5.1.1history^5.3.0Reading the three release posts and their migration guides (v6, v7, v8), the work breaks down as:
react-router-config,<Switch>, the controlled<Route location>,NavLink'sisActive/activeClassName,<Redirect>,useHistory, and the mutablehistoryobject are all gone, andmatchPathchanged signature.react-router-domintoreact-router.react-router-dommirror entirely (usereact-router) and makes the package ESM-only, targeting Node 22+ / React 19+ (we already ship React 19).Because Docusaurus uses React Router in declarative/library mode (not framework/data mode), almost none of the v7/v8 framework future-flags apply — the challenge is keeping the v5-era primitives Docusaurus depends on working, while reusing React Router's own APIs as much as possible.
Key changes
1.
react-router-configis gone → reuse React Router'smatchPath.renderRoutes/matchRoutes/RouteConfigno longer exist in v6+. Docusaurus' route config is string-based, recursive (routes), and order-based (first-match-wins), so:@docusaurus/renderRoutesis reimplemented as a small<Switch>-like component (no v8 equivalent for this pattern), preserving the existing absolute-path, first-match-wins recursion (renderRoutes(route.routes)).matchPath/matchRoutesis kept (the public@docusaurus/routerAPI uses the v5 signature), but built on top of React Router's ownmatchPathrather than hand-rolling path matching. The only extra logic is re-enforcing trailing-slash sensitivity forstrictroutes (v6'smatchPathhas nostrictoption).matchPathvia a dynamicimport('react-router').2. No more
historyobject /useHistoryin v6+ → drive RR with our own history.v6 hides history behind the router. Docusaurus instead creates a
history-package instance (browser/hash on the client, memory on the server) and drives React Router with the low-level<Router navigator={history}>— exactly whatBrowserRouterand the removedunstable_HistoryRouterdo internally.@docusaurus/router'suseHistory()returns it via context, keepinghistoryUtils(navigation blocking,listen, querystring helpers) working. This also lets us drop<StaticRouter>(SSR uses a memory history →useHistory()is uniform on client & server).3. Controlled
<Route location>is gone → location-override context.PendingNavigationused<Route location={...}>to keep rendering the previous route until the next one finished preloading. Replaced with a small location-override React context that@docusaurus/router'suseLocation()andrenderRoutesread.4.
NavLinklostisActive/activeClassName/activeStyle.These are heavily used by navbar items with the v5
isActive(match, location)signature. Docusaurus'<Link>now reimplements the active-state computation on top of React Router's<Link>+useLocation/matchPath, preserving the existing public API. Also:innerRef→ref(React 19), and a<Redirect>→<Navigate>compatibility shim.5.
historyv5 API tweaks.Historyis no longer generic, andblock()now receives a transition object (block-until-retry());historyUtilswas adapted accordingly.Temporary:
pnpm-workspace.yamladds aminimumReleaseAgeExcludeentry forreact-router@8.0.xbecause v8 was published only days ago (under the repo's 7-day install gate). This can be removed once v8 ages past the threshold.Note
Known follow-ups before this is merge-ready: type-safety cleanup, and two runtime behaviors not exercised by a production build — navigation blocking now re-arms after each allowed navigation (history v5
retrysemantics), anduseLocation()is now called in every<Link>(links re-render on navigation; worth optimizing for large pages).Test Plan
Local build of the Docusaurus website on the upgraded stack:
pnpm build:website:fastsucceeds: 982 HTML pages generated, and the broken-links checker passes (validates the newmatchPath-based matching for both routing and link validation).brokenLinkstest suite (incl.exact/stricttrailing-slash cases and the perf/optimization test from After updating to v3.1, large repo build takes 3 hours #9754) passes after being repointed to spy onreact-router'smatchPath./docs/introductionrenders the doc markdown withnavbar__link--active(NavLink active-state works during SSG), and blog posts render via nested routes. Only404.htmlcontains "Page Not Found" (the*catch-all), confirming routes resolve to real components.Test links
Deploy preview: https://deploy-preview-_____--docusaurus-2.netlify.app/
Relevant pages to review in the preview:
//docs/blogTabswithqueryString, version dropdown)/tests/hash-router pagesRelated issues/PRs
history→ v5.