Skip to content

Add support for React router v8#11289

Draft
smeng9 wants to merge 29 commits into
marmelab:nextfrom
smeng9:master
Draft

Add support for React router v8#11289
smeng9 wants to merge 29 commits into
marmelab:nextfrom
smeng9:master

Conversation

@smeng9

@smeng9 smeng9 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

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-dom

How To Test

Build RA packages locally, then link with apps in /examples folder

Additional Checks

  • The PR targets master for a bugfix or a documentation fix, or next for a feature
    next branch seems is disappeared, I assume master branch can be used for new features.
  • The PR includes unit tests (if not possible, describe why)
    Needs to be end-2-end tested with /examples folder.
  • The PR includes one or several stories (if not possible, describe why)
    Stories using the routers are updated.
  • The documentation is up to date

Also, please make sure to read the contributing guidelines.

@fzaninotto

Copy link
Copy Markdown
Member

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.

Shaoyu Meng and others added 9 commits June 23, 2026 07:13
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>
@smeng9

smeng9 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Hi @fzaninotto

The ra-router-react-router-v8 is pulled out to a separate package

@fzaninotto fzaninotto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Several remarks:

  • This is a new feature, so it must be PRed against nextand not master
  • 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')}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use test-ui's CreateButton?

@smeng9 smeng9 Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
<ListBase
resource="comments"
render={({ data }) => (
<ul>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use simpleList from test-ui

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<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 = () => (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by convention, the most basic story is called Basic in ra

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will change this to Basic story.

However the tanstack story does not follow the convention

The code was copied from there.

Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
Comment thread packages/ra-router-react-router-next/src/reactRouterNextProvider.stories.tsx Outdated
dataProvider={dataProvider}
layout={Layout}
>
<Resource name="posts" list={PostList} show={PostShow} />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same: use a customRoute to test this hook

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stories will be rewritten

* UseBlockerTest: blocks navigation while a form is dirty.
*/
const BlockerForm = () => {
const [dirty, setDirty] = React.useState(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you use useWarnWhenUnsavedChanges instead of reimplementing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

@smeng9

smeng9 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Hi @fzaninotto

Can you restore the next branch, then I can target this merge request against the correct branch? This branch is probably accidentally deleted in your repo.

I have already noted this in the additional checks.

@fzaninotto

Copy link
Copy Markdown
Member

Sure, the next branch is now up again.

@smeng9 smeng9 changed the base branch from master to next June 24, 2026 19:31
Shaoyu Meng and others added 9 commits June 24, 2026 15:26
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>
@smeng9 smeng9 changed the title Add support for React router v8 Draft: Add support for React router v8 Jun 25, 2026
@smeng9 smeng9 marked this pull request as draft June 25, 2026 02:21
@smeng9 smeng9 changed the title Draft: Add support for React router v8 Add support for React router v8 Jun 25, 2026
Shaoyu Meng and others added 7 commits June 24, 2026 22:56
…-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>
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