Skip to content

Commit 5be07b0

Browse files
authored
Merge pull request #11102 from marmelab/routerprovider
Add router abstraction and TanStack Router adapter
2 parents 625d03d + 128c4f0 commit 5be07b0

146 files changed

Lines changed: 6952 additions & 529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ build-ra-core:
4343
@echo "Transpiling ra-core files...";
4444
@cd ./packages/ra-core && yarn build
4545

46+
build-ra-router-tanstack:
47+
@echo "Transpiling ra-router-tanstack files...";
48+
@cd ./packages/ra-router-tanstack && yarn build
49+
4650
build-ra-ui-materialui:
4751
@echo "Transpiling ra-ui-materialui files...";
4852
@cd ./packages/ra-ui-materialui && yarn build
@@ -116,7 +120,7 @@ update-package-exports: ## Update the package.json "exports" field for all packa
116120
@echo "Updating package exports..."
117121
@yarn tsx ./scripts/update-package-exports.ts
118122

119-
build: build-ra-core 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
123+
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
120124

121125
doc: ## compile doc as html and launch doc web server
122126
@yarn doc

docs/Admin.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Here are all the props accepted by the component:
159159
| `queryClient` | Optional | `QueryClient` | - | The react-query client |
160160
| `ready` | Optional | `Component` | `Ready` | The content of the ready page |
161161
| `requireAuth` | Optional | `boolean` | `false` | Flag to require authentication for all routes |
162+
| `routerProvider` | Optional | `RouterProvider`| `reactRouterProvider`| The router provider for navigation |
162163
| `store` | Optional | `Store` | - | The Store for managing user preferences |
163164
| `theme` | Optional | `object` | `default LightTheme` | The main (light) theme configuration |
164165
| `title` | Optional | `string` | - | The error page title |
@@ -1050,6 +1051,28 @@ const App = () => (
10501051
);
10511052
```
10521053
1054+
## `routerProvider`
1055+
1056+
React-admin uses a router abstraction layer that allows you to choose between different routing libraries. By default, it uses [react-router](https://reactrouter.com/), but you can also use [TanStack Router](./TanStackRouter.md).
1057+
1058+
To use TanStack Router, pass the `tanStackRouterProvider` to the `routerProvider` prop:
1059+
1060+
```tsx
1061+
import { Admin, Resource } from 'react-admin';
1062+
import { tanStackRouterProvider } from 'ra-router-tanstack';
1063+
import { dataProvider } from './dataProvider';
1064+
1065+
const App = () => (
1066+
<Admin dataProvider={dataProvider} routerProvider={tanStackRouterProvider}>
1067+
<Resource name="posts" list={PostList} />
1068+
</Admin>
1069+
);
1070+
```
1071+
1072+
See the [TanStack Router documentation](./TanStackRouter.md) for more details on using TanStack Router with react-admin.
1073+
1074+
**Tip**: When using `tanStackRouterProvider`, navigation blocking (used by `warnWhenUnsavedChanges`) works out of the box, without requiring a Data Router setup.
1075+
10531076
## `store`
10541077
10551078
The `<Admin>` component initializes a [Store](./Store.md) for user preferences using `localStorage` as the storage engine. You can override this by passing a custom `store` prop.
@@ -1156,9 +1179,13 @@ const App = () => (
11561179
export default App;
11571180
```
11581181
1159-
## Using A Custom Router
1182+
## Using A Different Router Library
1183+
1184+
React-admin supports multiple routing libraries through its [router abstraction](./Routing.md). By default, it uses react-router, but you can also use [TanStack Router](./TanStackRouter.md) via the [`routerProvider`](#routerprovider) prop.
1185+
1186+
## Using A Custom react-router Configuration
11601187
1161-
React-admin uses [the react-router library](https://reactrouter.com/) to handle routing, with a [HashRouter](https://reactrouter.com/en/6/router-components/hash-router#hashrouter). This means that the hash portion of the URL (i.e. `#/posts/123` in the example) contains the main application route. This strategy has the benefit of working without a server, and with legacy web browsers.
1188+
By default, react-admin uses react-router with a [HashRouter](https://reactrouter.com/en/6/router-components/hash-router#hashrouter). This means that the hash portion of the URL (i.e. `#/posts/123` in the example) contains the main application route. This strategy has the benefit of working without a server, and with legacy web browsers.
11621189
11631190
But you may want to use another routing strategy, e.g. to allow server-side rendering of individual pages. React-router offers various Router components to implement such routing strategies. If you want to use a different router, simply put your app in a create router function. React-admin will detect that it's already inside a router, and skip its own router.
11641191

docs/AppBar.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,11 @@ export const MyAppBar = () => (
116116
</AppBar>
117117
);
118118
```
119-
119+
120120
If you omit `<TitlePortal>`, `<AppBar>` will no longer display the page title. This can be done on purpose, e.g. if you want to render something completely different in the AppBar, like a company logo and a search engine:
121121

122122
{% raw %}
123+
123124
```jsx
124125
// in src/MyAppBar.js
125126
import { AppBar } from 'react-admin';
@@ -137,6 +138,7 @@ const MyAppBar = () => (
137138
</AppBar>
138139
);
139140
```
141+
140142
{% endraw %}
141143

142144
## `color`
@@ -159,6 +161,7 @@ export const MyAppBar = () => <AppBar color="primary" />;
159161
Pass an `sx` prop to customize the style of the main component and the underlying elements (see [the `sx` documentation](./SX.md) for syntax and examples).
160162

161163
{% raw %}
164+
162165
```jsx
163166
// in src/MyAppBar.js
164167
import { AppBar } from 'react-admin';
@@ -172,6 +175,7 @@ export const MyAppBar = () => (
172175
/>
173176
);
174177
```
178+
175179
{% endraw %}
176180

177181
This property accepts the following subclasses:
@@ -194,7 +198,7 @@ By default, the `<AppBar>` renders three buttons in addition to the user menu:
194198

195199
If you want to reorder or remove these buttons, you can customize the toolbar by passing a `toolbar` prop.
196200

197-
```jsx
201+
```jsx
198202
// in src/MyAppBar.js
199203
import {
200204
AppBar,
@@ -246,17 +250,15 @@ If your app uses [authentication](./Authentication.md), the `<AppBar>` component
246250
Your browser does not support the video tag.
247251
</video>
248252

249-
250-
The content of the user menu depends on the return value of `authProvider.getIdentity()`. The user menu icon renders an anonymous avatar, or the `avatar` property of the identity object if present. If the identity object contains a `fullName` property, it is displayed after the avatar.
253+
The content of the user menu depends on the return value of `authProvider.getIdentity()`. The user menu icon renders an anonymous avatar, or the `avatar` property of the identity object if present. If the identity object contains a `fullName` property, it is displayed after the avatar.
251254

252255
You can customize the user menu by passing a `userMenu` prop to the `<AppBar>` component.
253256

254257
```tsx
255258
import * as React from 'react';
256-
import { AppBar, Logout, UserMenu, useUserMenu } from 'react-admin';
259+
import { AppBar, Logout, UserMenu, useUserMenu, LinkBase } from 'react-admin';
257260
import { MenuItem, ListItemIcon, ListItemText } from '@mui/material';
258261
import SettingsIcon from '@mui/icons-material/Settings';
259-
import { Link } from "react-router-dom";
260262

261263
// It's important to pass the ref to allow Material UI to manage the keyboard navigation
262264
const SettingsMenuItem = React.forwardRef<HTMLAnchorElement>((props, ref) => {
@@ -269,7 +271,7 @@ const SettingsMenuItem = React.forwardRef<HTMLAnchorElement>((props, ref) => {
269271
<MenuItem
270272
onClick={onClose}
271273
ref={ref}
272-
component={Link}
274+
component={LinkBase}
273275
to="/settings"
274276
// It's important to pass the props to allow Material UI to manage the keyboard navigation
275277
{...props}
@@ -299,6 +301,7 @@ Note that you still have to include the `<Logout>` component in the user menu, a
299301
You can also customize the default icon by setting the `icon` prop to the `<UserMenu />` component.
300302

301303
{% raw %}
304+
302305
``` jsx
303306
import { AppBar, UserMenu } from 'react-admin';
304307
import Avatar from '@mui/material/Avatar';
@@ -317,6 +320,7 @@ const MyUserMenu = props => (<UserMenu {...props} icon={<MyCustomIcon />} />);
317320

318321
const MyAppBar = () => <AppBar userMenu={<MyUserMenu />} />;
319322
```
323+
320324
{% endraw %}
321325

322326
Finally, you can hide the user menu by setting the `userMenu` prop to `false`.
@@ -454,6 +458,7 @@ export const MyAppbar = () => (
454458
If react-admin's `<AppBar>` component doesn't meet your needs, you can build your own component using Material UI's `<AppBar>`. Here is an example:
455459

456460
{% raw %}
461+
457462
```jsx
458463
// in src/MyAppBar.js
459464
import { AppBar, Toolbar, Box } from '@mui/material';
@@ -469,6 +474,7 @@ export const MyAppBar = () => (
469474
</AppBar>
470475
);
471476
```
477+
472478
{% endraw %}
473479

474480
Then, use your custom app bar in a custom `<Layout>` component:
@@ -494,4 +500,3 @@ By default, users can override the page title [in configurable mode](./Features.
494500
<source src="./img/TitleConfigurable.mp4" type="video/mp4"/>
495501
Your browser does not support the video tag.
496502
</video>
497-

docs/Architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ React-admin is specifically designed to build [Single-Page Applications (SPA)](h
1515

1616
The SPA architecture ensures that react-admin apps are [exceptionally fast](./Features.md#fast), easy to host, and compatible with existing APIs without requiring a dedicated backend.
1717

18-
To achieve this, react-admin utilizes an internal router, powered by `react-router`, to display the appropriate screen when the user clicks on a link. Developers can define routes using the [`<Resource>`](./Resource.md) component for CRUD routes and the [`<CustomRoutes>`](./CustomRoutes.md) component for other routes.
18+
To achieve this, react-admin utilizes an internal router to display the appropriate screen when the user clicks on a link. By default, this router is powered by [react-router](https://reactrouter.com/), but you can also use [TanStack Router](./TanStackRouter.md) through the `routerProvider` prop. Developers can define routes using the [`<Resource>`](./Resource.md) component for CRUD routes and the [`<CustomRoutes>`](./CustomRoutes.md) component for other routes.
1919

2020
For example, the following react-admin application:
2121

docs/Authentication.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,13 @@ For instance, to add a "forgot password" link to the login page:
252252

253253
```jsx
254254
import { Box, Link } from '@mui/material';
255-
import { Link as RouterLink } from 'react-router-dom';
256-
import { Login, LoginForm } from 'react-admin';
255+
import { Login, LoginForm, LinkBase } from 'react-admin';
257256

258257
const MyLogin = () => (
259258
<Login>
260259
<LoginForm />
261260
<Box textAlign="center" mb={1}>
262-
<Link component={RouterLink} to="/forgot-password">
261+
<Link component={LinkBase} to="/forgot-password">
263262
Forgot password?
264263
</Link>
265264
</Box>
@@ -272,7 +271,6 @@ const MyLogin = () => (
272271
You can also customize the login form fields, by setting the `LoginForm` children:
273272
274273
```jsx
275-
import { Link as RouterLink } from 'react-router-dom';
276274
import { Login, LoginForm, TextInput, PasswordInput, required } from 'react-admin';
277275

278276
const MyLogin = () => (
@@ -431,6 +429,7 @@ export const authProvider = {
431429
![Auth0 login flow diagram](./img/authProvider-OAuth-flow.png)
432430
{% comment %}
433431
Diagram source:
432+
434433
```mermaid
435434
sequenceDiagram
436435
autonumber
@@ -445,7 +444,8 @@ sequenceDiagram
445444
Note over RA: handleCallback()<br/>Auth0Client.handleRedirectCallback()
446445
RA->>U: Redirects to /posts
447446
```
448-
Edited with https://mermaid.live/edit
447+
448+
Edited with <https://mermaid.live/edit>
449449
{% endcomment %}
450450
451451
**Tip:** You can choose when to redirect users to the third-party authentication service, such as directly in the `AuthProvider.checkAuth()` method or when they click a button on a [custom login page](#customizing-the-login-component).

docs/AutoSave.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ The component renders nothing by default. It will save the current form values 3
6060

6161
```tsx
6262
import { AutoSave } from '@react-admin/ra-form-layout';
63-
import { Edit, PrevNextButton, SaveButton, SimpleForm, TextInput, Toolbar } from 'react-admin';
64-
import { useParams } from 'react-router';
63+
import { Edit, PrevNextButton, SaveButton, SimpleForm, TextInput, Toolbar, useParams } from 'react-admin';
6564

6665
const AutoSaveToolbar = () => (
6766
<Toolbar>

docs/Breadcrumb.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -885,9 +885,8 @@ Let's see how the components for the songs list and detail pages define their ap
885885
{% raw %}
886886
```jsx
887887
// in src/songs/SongList.js
888-
import { useGetOne, List, SearchInput, DataTable, DateField } from 'react-admin';
888+
import { useGetOne, List, SearchInput, DataTable, DateField, useParams } from 'react-admin';
889889
import { useDefineAppLocation } from '@react-admin/ra-navigation';
890-
import { useParams } from 'react-router-dom';
891890

892891
export const SongList = () => {
893892
const { id } = useParams();
@@ -929,9 +928,8 @@ const EditSongButton = () => {
929928
930929
```jsx
931930
// in src/songs/SongDetail.js
932-
import { useGetOne, Edit, SimpleForm, TextInput, DateInput } from 'react-admin';
931+
import { useGetOne, Edit, SimpleForm, TextInput, DateInput, useParams } from 'react-admin';
933932
import { useDefineAppLocation } from '@react-admin/ra-navigation';
934-
import { useParams } from 'react-router-dom';
935933
936934
export const SongDetail = () => {
937935
const { id, songId } = useParams();

0 commit comments

Comments
 (0)