Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9ef5fdc
support react-router v8
Jun 22, 2026
6c3b189
update comments
Jun 22, 2026
352bcd5
update lock file
Jun 22, 2026
0df7e82
fix missing error boundary tests
Jun 23, 2026
f515372
feat: Add ra-router-react-router-v8 adapter package
Jun 23, 2026
d8b41fc
docs: Note deferred unit tests pending React 19 test lane
Jun 23, 2026
cd163ee
Merge branch 'feature/react-router-v8' into react-router-v8-old
Jun 23, 2026
eb6032e
refactor: Drop in-core react-router compat layer on react-router-v8-old
Jun 23, 2026
e85b130
build: Keep react-router ^8.0.0 in the peer/dependency range
Jun 23, 2026
69955ef
build: Remove unused react-router-dom from ra-ui-materialui
Jun 23, 2026
802469e
fix: Restore canonical nested exports for ra-core and ra-ui-materialui
Jun 23, 2026
cb85c7d
build: Exclude ESM-only ra-router-react-router-v8 from update-package…
Jun 23, 2026
61e650d
build: Drop redundant module/moduleResolution from v8 adapter tsconfig
Jun 23, 2026
eb8cabf
feat: Extract react-router adapters into standalone packages
Jun 24, 2026
0868f54
refactor: Export reactRouterProvider from ra-core root only
Jun 24, 2026
060de5e
docs: List reactRouterNextProvider in routing README components
Jun 24, 2026
0cbc7ca
docs: Reference react-router instead of react-router-dom in routing R…
Jun 24, 2026
e1a62e4
feat: Support react-router v6 and v7 in the default adapter
Jun 24, 2026
ff4e528
test: Mirror ra-router-tanstack stories for ra-router-react-router-next
Jun 25, 2026
ba20638
update react-router provider comments
Jun 25, 2026
689e5dd
test: Add unit tests for the React Router v8 adapter
Jun 25, 2026
84ea0f5
test: Apply review feedback to React Router v8 adapter stories
Jun 25, 2026
f8f4b69
test: Mirror ra-router-tanstack stories and tests for ra-router-react…
Jun 25, 2026
fbd10e8
test: Use built-in unsaved-changes guard in React Router v8 adapter s…
Jun 25, 2026
4839c64
test: Mirror routing stories and tests into ra-router-react-router
Jun 25, 2026
2bbf948
test: Mirror ra-router-tanstack spec coverage in ra-router-react-rout…
Jun 25, 2026
aba3a03
test: Mirror the comprehensive provider spec in ra-router-react-router
Jun 25, 2026
3b1e367
fix: Prepend basename in react-router-next Link/Navigate/useNavigate
Jun 25, 2026
42ff222
fix: Prepend basename in react-router Link/Navigate/useNavigate
Jun 25, 2026
12d951c
test: Drop StandaloneWithBasename story from the router adapters
Jun 25, 2026
1345d92
make path relative
Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ build-offline: ## build the offline example
preview-offline: ## preview the offline example
@yarn preview-offline

build-ra-router-react-router:
@echo "Transpiling ra-router-react-router files...";
@cd ./packages/ra-router-react-router && yarn build

build-ra-core:
@echo "Transpiling ra-core files...";
@cd ./packages/ra-core && yarn build
Expand All @@ -56,6 +60,10 @@ build-ra-router-tanstack:
@echo "Transpiling ra-router-tanstack files...";
@cd ./packages/ra-router-tanstack && yarn build

build-ra-router-react-router-next:
@echo "Transpiling ra-router-react-router-next files...";
@cd ./packages/ra-router-react-router-next && yarn build

build-ra-ui-materialui:
@echo "Transpiling ra-ui-materialui files...";
@cd ./packages/ra-ui-materialui && yarn build
Expand Down Expand Up @@ -129,7 +137,7 @@ update-package-exports: ## Update the package.json "exports" field for all packa
@echo "Updating package exports..."
@yarn tsx ./scripts/update-package-exports.ts

build: build-ra-core build-ra-router-tanstack build-ra-data-fakerest build-ra-ui-materialui build-ra-data-json-server build-ra-data-local-forage build-ra-data-local-storage build-ra-data-simple-rest build-ra-data-graphql build-ra-data-graphql-simple build-ra-input-rich-text build-data-generator build-ra-language-english build-ra-language-french build-ra-i18n-i18next build-ra-i18n-polyglot build-react-admin build-ra-no-code build-create-react-admin update-package-exports ## compile ES6 files to JS
build: build-ra-router-react-router build-ra-core build-ra-router-tanstack build-ra-router-react-router-next build-ra-data-fakerest build-ra-ui-materialui build-ra-data-json-server build-ra-data-local-forage build-ra-data-local-storage build-ra-data-simple-rest build-ra-data-graphql build-ra-data-graphql-simple build-ra-input-rich-text build-data-generator build-ra-language-english build-ra-language-french build-ra-i18n-i18next build-ra-i18n-polyglot build-react-admin build-ra-no-code build-create-react-admin update-package-exports ## compile ES6 files to JS

typecheck: ## check TypeScript types
@yarn typecheck
Expand Down
59 changes: 59 additions & 0 deletions docs/ReactRouterV8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
layout: default
title: "React Router v8 Integration"
---

# React Router v8 Integration

By default, react-admin is powered by [React Router](https://reactrouter.com/) v6/v7 through the `ra-router-react-router` adapter, which is installed automatically. React Router v8 merged the former `react-router-dom` package into `react-router` and requires **React 19**, so support for it ships as a separate, opt-in adapter package: `ra-router-react-router-next`.

**New projects are encouraged to run on React Router v8 using `ra-router-react-router-next`.** The React Router v6/v7 adapter remains the default for backward compatibility, and **`ra-router-react-router-next` will become the default `ra-router-react-router` in react-admin v6.**

Use this package when your application runs on React Router v8.

## Installation

```bash
npm install ra-router-react-router-next react-router@^8
# or
yarn add ra-router-react-router-next react-router@^8
```

React Router v8 requires React 19. Make sure your application uses `react@^19.2.7` and `react-dom@^19.2.7`.

## Configuration

Set the `<Admin routerProvider>` to `reactRouterNextProvider`:

```tsx
import { Admin, Resource, ListGuesser } from 'react-admin';
import { reactRouterNextProvider } from 'ra-router-react-router-next';
import { dataProvider } from './dataProvider';

const App = () => (
<Admin
dataProvider={dataProvider}
routerProvider={reactRouterNextProvider}
>
<Resource name="posts" list={ListGuesser} />
<Resource name="comments" list={ListGuesser} />
</Admin>
);

export default App;
```

That's it! React-admin will now use React Router v8 for all routing operations.

## Standalone Mode

When using `reactRouterNextProvider` without an existing React Router, react-admin creates its own hash router automatically (URLs like `/#/posts`). This is called **standalone mode**. This is the simplest setup and requires no additional configuration.

## Embedded Mode

If your application already uses React Router, you can embed react-admin inside it. React-admin detects the existing router context and uses it instead of creating its own.

## When To Use This Package

- Use the **default** `ra-router-react-router` adapter (installed automatically, no extra setup) if your app runs on React Router v6 or v7.
- Use **`ra-router-react-router-next`** if your app runs on React Router v8 (and therefore React 19). This is the recommended choice for new projects, and it will become the default in react-admin v6.
2 changes: 1 addition & 1 deletion docs/Routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const MyLayout = ({ children }) => {

## Using A Different Router Library

React-admin supports multiple routing libraries through its router abstraction layer. By default, it uses react-router with a [HashRouter](https://reactrouter.com/en/routers/create-hash-router). You can also use [TanStack Router](./TanStackRouter.md) as an alternative.
React-admin supports multiple routing libraries through its router abstraction layer. By default, it uses react-router (v6/v7) through the [`ra-router-react-router`](./ReactRouterV8.md) adapter with a [HashRouter](https://reactrouter.com/en/routers/create-hash-router). You can also use [TanStack Router](./TanStackRouter.md) or [React Router v8](./ReactRouterV8.md) as an alternative.

To use TanStack Router:

Expand Down
54 changes: 31 additions & 23 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,37 @@ const moduleNameMapper = packages.reduce((mapper, dirName) => {
}, {});

module.exports = {
globalSetup: './test-global-setup.js',
setupFilesAfterEnv: ['./test-setup.js'],
testEnvironment: 'jsdom',
testPathIgnorePatterns: [
'/node_modules/',
'/lib/',
'/esm/',
'/examples/simple/',
'/packages/create-react-admin/templates',
],
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\](?!(@hookform|react-hotkeys-hook|@faker-js/faker)/).+\\.(js|jsx|mjs|ts|tsx)$',
],
transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
'^.+\\.[tj]sx?$': [
'ts-jest',
{
isolatedModules: true,
useESM: true,
projects: [
{
globalSetup: './test-global-setup.js',
setupFilesAfterEnv: ['./test-setup.js'],
testEnvironment: 'jsdom',
testPathIgnorePatterns: [
'/node_modules/',
'/lib/',
'/esm/',
'/examples/simple/',
'/packages/create-react-admin/templates',
'/packages/ra-router-react-router-next/',
],
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\](?!(@hookform|react-hotkeys-hook|@faker-js/faker)/).+\\.(js|jsx|mjs|ts|tsx)$',
],
transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
'^.+\\.[tj]sx?$': [
'ts-jest',
{
isolatedModules: true,
useESM: true,
},
],
},
],
},
moduleNameMapper,
moduleNameMapper,
},
// ra-router-react-router-next can't be compiled into cjs.
// Running its tests requires `NODE_OPTIONS=--experimental-vm-modules`.
'./packages/ra-router-react-router-next/jest.config.cjs',
],
testTimeout: 60000,
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"build": "lerna run build",
"typecheck": "CI=true lerna run build",
"watch": "lerna run --parallel watch",
"test-unit": "cross-env LANG=en_US.UTF-8 NODE_ENV=test cross-env BABEL_ENV=cjs NODE_ICU_DATA=./node_modules/full-icu jest",
"test-unit-ci": "cross-env LANG=en_US.UTF-8 NODE_ENV=test cross-env BABEL_ENV=cjs NODE_ICU_DATA=./node_modules/full-icu jest --runInBand",
"test-unit": "cross-env LANG=en_US.UTF-8 NODE_ENV=test cross-env BABEL_ENV=cjs NODE_ICU_DATA=./node_modules/full-icu NODE_OPTIONS=--experimental-vm-modules jest",
"test-unit-ci": "cross-env LANG=en_US.UTF-8 NODE_ENV=test cross-env BABEL_ENV=cjs NODE_ICU_DATA=./node_modules/full-icu NODE_OPTIONS=--experimental-vm-modules jest --runInBand",
"test-e2e": "yarn run -s build && cross-env NODE_ENV=test && cd cypress && yarn test",
"test-e2e-local": "cd cypress && yarn start",
"test": "yarn test-unit && yarn test-e2e",
Expand Down
5 changes: 2 additions & 3 deletions packages/ra-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@
"@tanstack/react-query": "^5.83.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"react-hook-form": "^7.72.0",
"react-router": "^6.28.1 || ^7.1.1",
"react-router-dom": "^6.28.1 || ^7.1.1"
"react-hook-form": "^7.72.0"
},
"dependencies": {
"date-fns": "^3.6.0",
Expand All @@ -76,6 +74,7 @@
"jsonexport": "^3.2.0",
"lodash": "^4.17.21",
"query-string": "^7.1.3",
"ra-router-react-router": "^5.14.7",
"react-error-boundary": "^4.0.13",
"react-is": "^18.2.0 || ^19.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/Authenticated.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { Routes, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router';

import { memoryStore } from '../store';
import { CoreAdminContext } from '../core';
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/useAuthenticated.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { Routes, Route, useLocation } from 'react-router-dom';
import { Routes, Route, useLocation } from 'react-router';
import { memoryStore } from '../store';

import { useNotificationContext } from '../notification';
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/useHandleAuthCallback.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';

import { useHandleAuthCallback } from './useHandleAuthCallback';
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/useLogin.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { Routes, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router';
import expect from 'expect';

import { CoreAdminContext } from '../core/CoreAdminContext';
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/useLogout.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { Routes, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router';
import { QueryClient } from '@tanstack/react-query';
import expect from 'expect';

Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/auth/useLogoutIfAccessDenied.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { useEffect, useState } from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { Routes, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router';

import useLogoutIfAccessDenied from './useLogoutIfAccessDenied';
import { AuthContext } from './AuthContext';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@testing-library/react';
import expect from 'expect';
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router';

import {
AuthProvider,
Expand Down
7 changes: 3 additions & 4 deletions packages/ra-core/src/controller/usePrevNextController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ import { useCreatePath } from '../routing';
*
* @example <caption>Custom PrevNextButton</caption>
*
* import { UsePrevNextControllerProps, useTranslate } from 'ra-core';
* import { UsePrevNextControllerProps, useTranslate, LinkBase as Link } from 'ra-core';
* import NavigateBefore from '@mui/icons-material/NavigateBefore';
* import NavigateNext from '@mui/icons-material/NavigateNext';
* import ErrorIcon from '@mui/icons-material/Error';
* import { Link } from 'react-router-dom';
* import { CircularProgress, IconButton } from '@mui/material';
*
* const MyPrevNextButtons = props => {
Expand Down Expand Up @@ -78,7 +77,7 @@ import { useCreatePath } from '../routing';
* <li>
* <IconButton
* component={hasPrev ? Link : undefined}
* to={navigateToPrev}
* to={prevPath}
* aria-label={translate('ra.navigation.previous')}
* disabled={!hasPrev}
* >
Expand All @@ -93,7 +92,7 @@ import { useCreatePath } from '../routing';
* <li>
* <IconButton
* component={hasNext ? Link : undefined}
* to={navigateToNext}
* to={nextPath}
* aria-label={translate('ra.navigation.next')}
* disabled={!hasNext}
* >
Expand Down
10 changes: 3 additions & 7 deletions packages/ra-core/src/core/CoreAdminContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import * as React from 'react';
import { useMemo } from 'react';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';

import {
AdminRouter,
RouterProviderContext,
RouterProvider,
reactRouterProvider,
} from '../routing';
import { reactRouterProvider } from 'ra-router-react-router';
import { AdminRouter, RouterProviderContext, RouterProvider } from '../routing';
import { AuthContext, convertLegacyAuthProvider } from '../auth';
import {
DataProviderContext,
Expand Down Expand Up @@ -174,7 +170,7 @@ export interface CoreAdminContextProps {
* The router provider for custom routing implementations
*
* Use this to integrate react-admin with alternative routers like TanStack Router.
* Defaults to react-router-dom.
* Defaults to react-router.
*
* @see https://marmelab.com/react-admin/Admin.html#routerprovider
* @example
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/core/CoreAdminRoutes.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import expect from 'expect';
import { Route } from 'react-router-dom';
import { Route } from 'react-router';

import { CoreAdminContext } from './CoreAdminContext';
import { RouterNavigateFunction, TestMemoryRouter } from '../routing';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import expect from 'expect';
import { Route } from 'react-router-dom';
import { Route } from 'react-router';
import { useResourceDefinitions } from './useResourceDefinitions';

import { CoreAdminContext } from './CoreAdminContext';
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-core/src/dataProvider/useGetRecordId.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { useGetRecordId } from './useGetRecordId';
import { render, screen } from '@testing-library/react';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router';
import { RecordContextProvider } from '../controller';

import { TestMemoryRouter } from '../routing';
Expand Down
12 changes: 3 additions & 9 deletions packages/ra-core/src/form/Form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ import * as yup from 'yup';
import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from 'ra-language-english';
import fakeRestDataProvider from 'ra-data-fakerest';
import {
Route,
Routes,
useNavigate,
Link,
HashRouter,
useLocation,
} from 'react-router-dom';
import { Route, Routes, useNavigate, useLocation } from 'react-router';
import { HashRouter } from 'react-router-dom';

import { CoreAdminContext } from '../core';
import {
Expand All @@ -32,7 +26,7 @@ import { useInput } from './useInput';
import { required, ValidationError } from './validation';
import { mergeTranslations } from '../i18n';
import { I18nProvider, RaRecord } from '../types';
import { TestMemoryRouter } from '../routing';
import { TestMemoryRouter, LinkBase as Link } from '../routing';
import { useNotificationContext } from '../notification';

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import expect from 'expect';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { useForm, useFormContext, FormProvider } from 'react-hook-form';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router';
import { TestMemoryRouter, useNavigate, useParams } from '../routing';

import { useWarnWhenUnsavedChanges } from './useWarnWhenUnsavedChanges';
Expand Down
2 changes: 2 additions & 0 deletions packages/ra-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export * from './store';
export * from './types';
export * from './util';
export * as testUI from './test-ui';

export * from 'ra-router-react-router';
Loading
Loading