Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Fix nested routes explanation and add more examples #8616

Merged
merged 2 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
183 changes: 157 additions & 26 deletions docs/CustomRoutes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@ title: "The CustomRoutes Component"

# `<CustomRoutes>`

Lets you define custom pages in your react-admin application, using [react-router-dom](https://reactrouter.com/docs/en/v6/api#routes-and-route) `<Routes>` elements.

## Usage

To register your own routes, pass one or several `<CustomRoutes>` elements as children of `<Admin>`. Declare as many [react-router-dom](https://reactrouter.com/docs/en/v6/api#routes-and-route) `<Route>` as you want inside them.
Alternatively, you can add your custom routes to resources. They will be available under the resource prefix.

```jsx
// in src/App.js
import * as React from "react";
import { Admin, Resource, CustomRoutes } from 'react-admin';
import { Route } from "react-router-dom";
import simpleRestProvider from 'ra-data-simple-rest';
import posts, { PostAnalytics } from './posts';

import dataProvider from './dataProvider';
import posts from './posts';
import comments from './comments';
import Settings from './Settings';
import Profile from './Profile';

const App = () => (
<Admin dataProvider={simpleRestProvider('http://path.to.my.api')}>
<Resource name="posts" {...posts}>
<Route path="analytics" element={<PostAnalytics/>} />
</Resource>
<Admin dataProvider={dataProvider}>
<Resource name="posts" {...posts} />
<Resource name="comments" {...comments} />
<CustomRoutes>
<Route path="/settings" element={<Settings />} />
Expand All @@ -37,7 +39,63 @@ export default App;

Now, when a user browses to `/settings` or `/profile`, the components you defined will appear in the main part of the screen.

**Tip**: To look like other react-admin pages, your custom pages should have the following structure:
## `children`

`children` of the `<CustomRoutes>` component must be `<Route>` elements from [react-router-dom](https://reactrouter.com/docs/en/v6/api#routes-and-route), and map a path with a custom element.

```jsx
// in src/App.js
import { Admin, Resource, CustomRoutes } from 'react-admin';
import { Route } from "react-router-dom";

import dataProvider from './dataProvider';
import Settings from './Settings';
import Profile from './Profile';

const App = () => (
<Admin dataProvider={dataProvider}>
<CustomRoutes>
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</CustomRoutes>
</Admin>
);

export default App;
```

## `noLayout`

By default, custom routes render within the application layout (with the menu and the app bar). If you want a custom route to render without the layout, e.g. for registration screens, then provide the `noLayout` prop on the `<CustomRoutes>` element:

```jsx
// in src/App.js
import { Admin, CustomRoutes } from 'react-admin';
import { Route } from "react-router-dom";

import dataProvider from './dataProvider';
import Register from './Register';
import Settings from './Settings';
import Profile from './Profile';

const App = () => (
<Admin dataProvider={dataProvider}>
<CustomRoutes noLayout>
<Route path="/register" element={<Register />} />
</CustomRoutes>
<CustomRoutes>
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</CustomRoutes>
</Admin>
);
```

As illustrated above, there can be more than one `<CustomRoutes>` element inside an `<Admin>` component.

## Custom Page Title

To define the page title (displayed in the app bar), your custom pages can use the `<Title>` component from react-admin:

```jsx
// in src/Settings.js
Expand All @@ -57,34 +115,107 @@ const Settings = () => (
export default Settings;
```

**Tip**: It's up to you to create a [custom menu](./Theming.md#using-a-custom-menu) entry, or custom buttons, that lead to your custom pages.
`<Title>` uses a [React Portal](https://reactjs.org/docs/portals.html), so it doesn't matter *where* you put it in your component. The title will always be rendered in the app bar.

## Linking To Custom Routes

If you want a custom route to render without the layout (without the menu and the appBar), e.g. for registration screens, then provide the `noLayout` prop on the `<CustomRoutes>` element:
You can link to your pages using [react-router's Link component](https://reactrouter.com/en/main/components/link):

```jsx
// in src/App.js
import * as React from "react";
import { Admin, CustomRoutes } from 'react-admin';
import simpleRestProvider from 'ra-data-simple-rest';
import { Link as RouterLink } from 'react-router-dom';
import { Link } from '@mui/material';

const SettingsButton = () => (
<Link component={RouterLink} to="/settings">
Settings
</Link>
);
```

Alternately, create a [custom menu](./Menu.md) with entries for the custom pages.

```jsx
// in src/MyMenu.js
import { Menu } from 'react-admin';
import SettingsIcon from '@mui/icons-material/Settings';
import PeopleIcon from '@mui/icons-material/People';

export const MyMenu = () => (
<Menu>
<Menu.DashboardItem />
<Menu.ResourceItem to="/posts" />
<Menu.ResourceItem to="/comments" />
<Menu.Item to="/settings" primaryText="Users" leftIcon={<SettingsIcon />}/>
<Menu.Item to="/profile" primaryText="Miscellaneous" leftIcon={<PeopleIcon />}/>
</Menu>
);
```

## Sub-Routes

If you want to add sub-routes to a resource, add the `<Route>` elements as [children of the `<Resource>` element](./Resource.md#children):

```jsx
import { Admin, Resource } from 'react-admin';
import { Route } from "react-router-dom";
import Settings from './Settings';
import Register from './register';

import dataProvider from './dataProvider';
import posts from './posts';

const App = () => (
<Admin dataProvider={simpleRestProvider('http://path.to.my.api')}>
<CustomRoutes noLayout>
<Route path="/register" element={<Register />} />
</CustomRoutes>
<CustomRoutes noLayout>
<Route path="/settings" element={<Settings />} />
</CustomRoutes>
<Admin dataProvider={dataProvider}>
<Resource name="posts" {...posts}>
<Route path="analytics" element={<PostAnalytics/>} />
</Resource>
</Admin>
);

export default App;
// is equivalent to
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" {...posts} />
<CustomRoutes>
<Route path="/posts/analytics" element={<PostAnalytics />} />
</CustomRoutes>
</Admin>
);
```

When a user browses to `/register`, the `<Register>` component will appear outside the defined Layout, leaving you the freedom to design the screen the way you want.
This is usually useful for nested resources, such as books on authors:

{% raw %}
```jsx
// in src/App.jss
import { Admin, Resource, ListGuesser, EditGuesser } from 'react-admin';
import { Route } from "react-router-dom";

const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="authors" list={ListGuesser} edit={EditGuesser}>
<Route path=":authorId/books" element={<BookList />} />
</Resource>
</Admin>
);

// in src/BookList.jss
import { useParams } from 'react-router-dom';
import { List, Datagrid, TextField } from 'react-admin';

const BookList = () => {
const { authorId } = useParams();
return (
<List resource="books" filter={{ authorId }}>
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="title" />
<TextField source="year" />
</Datagrid>
</List>
);
};
```
{% endraw %}

**Tip**: Custom routes can be [a `<Navigate>` route](https://reactrouter.com/docs/en/v6/api#navigate), too.
**Tip**: In the above example, the `resource="books"` prop is required in `<List>` because the `ResourceContext` defaults to `authors` inside the `<Resource name="authors">`.

Check [the `<Resource>` element documentation](./Resource.md#children) for more information.
Loading