Add support for React router v8#11289
Conversation
|
Thanks for your PR, but U'm not fan of the extra compatibility layer & the partial reimplementation. I'd prefer if you created an entirely new adapter for react-router v8 (in a new package). We'd probably need to move the current routing adapter to a package, too. |
React Router v8 merged react-router-dom into react-router and requires React 19. Rather than adding a compatibility layer and partial reimplementation inside ra-core (per review feedback on marmelab#11289), v8 support ships as a standalone, opt-in adapter package mirroring ra-router-tanstack. The adapter is a thin pass-through over react-router v8's native API: the obsolete v6/v7 `future` flags are dropped (now defaults in v8) and imports come from `react-router` instead of `react-router-dom`. ra-core keeps its built-in react-router v6/v7 adapter as the default, so existing apps are unaffected. The package is ESM-only because react-router v8 is ESM-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After merging the ra-router-react-router-v8 package, the in-core react-router adapter no longer needs the compatibility shims: - reactRouterProvider imports everything from `react-router`, with only `Link` and `createHashRouter` from `react-router-dom` - Form.stories uses `HashRouter` from `react-router-dom` directly - Remove CompatHashRouter, CompatLink and their tests - Restore `react-router-dom` to the dependencies of ra-core, ra-ui-materialui and react-admin, and narrow the react-router range back to ^6.28.1 || ^7.1.1 (v8 is supported via the separate package) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No code in ra-ui-materialui imports react-router-dom; all routing imports come from react-router. ra-core declares its own react-router-dom peer for its router adapter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Individual zshy builds had rewritten the exports field to the flat pre-normalization form. Restore the nested import/require form that update-package-exports produces and that matches upstream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-exports update-package-exports forces dual import/require exports on every package; the ESM-only v8 adapter must keep its ESM-only exports (it has no .cjs build). Also keep minor README/story/doc wording. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
zshy's ESM build pass already uses Bundler resolution and cjs is disabled, so these compilerOptions are not needed for the build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hi @fzaninotto The ra-router-react-router-v8 is pulled out to a separate package |
fzaninotto
left a comment
There was a problem hiding this comment.
Thanks. Several remarks:
- This is a new feature, so it must be PRed against
nextand notmaster - As RR v7 is still loaded by default by ra-core, users of RRv8 will end up with 2 versions of RR, leading to probable context conflicts. You also need to extract the default RRv7 adapter to a package in the monorepo
- The new RRv8 adapter needs unit tests in addition to user stories
- you also need to test the basename feature (check the tests of the tanstack router adapter)
| </li> | ||
| ))} | ||
| </ul> | ||
| <button onClick={() => navigate('/posts/create')}> |
There was a problem hiding this comment.
why not use test-ui's CreateButton?
There was a problem hiding this comment.
Ah previously this is mirroring the tanStackRoutherProvider.stories.tsx https://github.com/marmelab/react-admin/blob/master/packages/ra-router-tanstack/src/tanStackRouterProvider.stories.tsx#L80
Their code uses <button> instead of <CreateButton>
Will try to apply code review suggestions anyway.
| <ListBase | ||
| resource="comments" | ||
| render={({ data }) => ( | ||
| <ul> |
There was a problem hiding this comment.
<ul> and <li> are used previously because it is a mirror of
Can be updated to simpleList from test-ui
| * BasicStandalone: react-admin runs on its own hash router created by the | ||
| * react-router v8 provider (no surrounding router). | ||
| */ | ||
| export const BasicStandalone = () => ( |
There was a problem hiding this comment.
by convention, the most basic story is called Basic in ra
There was a problem hiding this comment.
Will change this to Basic story.
However the tanstack story does not follow the convention
The code was copied from there.
| dataProvider={dataProvider} | ||
| layout={Layout} | ||
| > | ||
| <Resource name="posts" list={PostList} show={PostShow} /> |
There was a problem hiding this comment.
same: use a customRoute to test this hook
There was a problem hiding this comment.
The stories will be rewritten
| * UseBlockerTest: blocks navigation while a form is dirty. | ||
| */ | ||
| const BlockerForm = () => { | ||
| const [dirty, setDirty] = React.useState(false); |
There was a problem hiding this comment.
I suggest you use useWarnWhenUnsavedChanges instead of reimplementing it.
|
Hi @fzaninotto Can you restore the I have already noted this in the additional checks. |
|
Sure, the |
Move the default react-router v6 provider out of ra-core into a new ra-router-react-router package, and rename the React Router v8 adapter from ra-router-react-router-v8 to ra-router-react-router-next. Why: - Decouples react-router from ra-core/ra-ui-materialui/react-admin: they now keep react-router only as a devDependency (for tests), while the adapter package carries the runtime dependency. This lets the router library evolve independently of the framework packages. - Sets up the v6 migration path: ra-router-react-router-next (v8) is the recommended choice for new projects and will be republished as ra-router-react-router in react-admin v6, while the v6 default is kept for backward compatibility. The new package is a dependency-free leaf (it mirrors ra-core's RouterProvider contract with local types instead of importing it) so it can build before ra-core, avoiding a circular build dependency; ra-core enforces interface conformance at the consumption site. ra-core and react-admin now depend on ra-router-react-router; ra-core re-exports reactRouterProvider for backward compatibility. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the reactRouterProvider re-export from the routing barrel and expose it once via `export * from 'ra-router-react-router'` at the end of ra-core's index. Internal consumers (CoreAdminContext) now import it directly from the package. Why: keeps a single, explicit source for the re-exported adapter instead of duplicating the re-export inside the routing module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the react-router v8 opt-in provider (ra-router-react-router-next) to the Key Components tree so it matches the Key Files Reference table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…EADME Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Declare react-router/react-router-dom as `^6.28.1 || ^7.1.1` in both the dependencies and peerDependencies of ra-router-react-router, while pinning the build to v6 via devDependencies. This restores the v6/v7 support range for consumers while keeping the monorepo on a single hoisted v6 instance (so the adapter builds against v6 and shares react-router with ra-core, avoiding the duplicate-instance breakage a bare `^6 || ^7` range caused). Also restore the "v6/v7" wording across the README and docs to match the declared range, and drop a now-obvious build-order comment in the Makefile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the React Router v8 adapter stories to follow the ra-router-tanstack story set 1:1 (same scenarios, helpers, and sample data), adapting only the embedded-router story to React Router v8's createHashRouter/RouterProvider. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
React Router v8 is ESM-only and requires React 19, neither of which fits the default CommonJS jest project. Add a dedicated jest project for ra-router-react-router-next (its own jest.config.cjs, wired into the root config via `projects`): it runs in ESM mode, transforms react-router, and forces React 19 (a new devDependency of the package) as a single instance across the tree to avoid duplicate-React hook errors. The test scripts now pass `NODE_OPTIONS=--experimental-vm-modules`. The new spec mirrors the tanstack adapter tests: matchPath (including basename scenarios), RouterWrapper standalone/embedded with a basename, and the routing hooks/components exercised through the stories. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address the PR review comments on the stories: - use test-ui components (SimpleList, SimpleShowLayout, SimpleForm, CreateButton) instead of hand-rolled markup - rename the basic story to `Basic` (ra convention) - test Link and the routing hooks (useParams/useMatch/useLocation/ useInRouterContext/useCanBlock) through CustomRoutes with no resource - drop the redundant stories (the duplicate "basic" variant and the redirect-to-first-resource story, which is already react-admin's default) - let EditBase read the route params instead of reading them manually - use the built-in useWarnWhenUnsavedChanges (via Form's warnWhenUnsavedChanges) instead of reimplementing navigation blocking The spec is updated to match the reworked stories. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-router-next Port the full set of routing stories from ra-router-tanstack to the React Router v8 adapter so coverage matches across both providers, and update the spec to exercise every story. The embedded-router story now mirrors tanstack's structure, splitting the route tree into named pieces and creating the router inside the component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tories Replace the hand-rolled useBlocker form story with one that relies on react-admin's useWarnWhenUnsavedChanges (via SimpleForm's warnWhenUnsavedChanges prop), inline PostShow fields, and drop the unused jest project displayName. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy the full ra-router-react-router-next story suite and its spec into the react-router v6/v7 adapter so both providers share identical coverage. Only the Storybook title, the provider identifier, and the host-router imports (react-router / react-router-dom split) differ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er-next Rewrite the spec to follow the tanstack provider spec structure (full matchPath suite plus all hook/component describes), adapting expectations to react-router's real semantics. The embedded basename deep-navigation case is documented and omitted, as the adapter relies on react-router's native router-level basename. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the react-router v6/v7 adapter spec in line with ra-router-react-router-next (full matchPath suite plus all hook/component describes). react-router's matchPath behaves identically here, so all 106 tests pass unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The adapter relied solely on react-router's native router-level basename, which only exists in standalone mode. In embedded mode react-admin owns the basename (exposed via useBasename) while the host router has none, so internal links escaped the basename and 404'd. Prepend the basename in Link, Navigate, and useNavigate (guarded against double-prepend); native Routes need no change. Restores the embedded nested-navigation test and adds a standalone-with-basename guard test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror the react-router-next basename fix in the default v6/v7 adapter so embedded react-admin (mounted under a basename) resolves internal links correctly. Because ra-core depends on this package, it cannot import ra-core's useBasename (circular build dependency); instead RouterWrapper publishes the basename via a provider-local context that Link, Navigate, and useNavigate read. Standalone mode keeps the basename on the hash router and leaves the context empty to avoid double-prepending. Adds the embedded nested-navigation and standalone-with-basename tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
react-router v8 is out
Solution
Follow #10440
Add support for React router v8
Find a way to avoid dependency resolution issue after dropping
react-router-domHow To Test
Build RA packages locally, then link with apps in /examples folder
Additional Checks
masterfor a bugfix or a documentation fix, ornextfor a featurenextbranch seems is disappeared, I assumemasterbranch can be used for new features.Needs to be end-2-end tested with /examples folder.
Stories using the routers are updated.
Also, please make sure to read the contributing guidelines.