The <Resource>
component
<Resource>
components define the CRUD routes of a react-admin application.
In react-admin terms, a resource is a string that refers to an entity type (like âproductsâ, âsubscribersâ, or âtagsâ). Records are objects with an id
field, and two records of the same resource have the same field structure (e.g. all posts records have a title, a publication date, etc.).
A <Resource>
component has 3 responsibilities:
- It defines the CRUD routes of a given resource (to display a list of records, the details of a record, or to create a new one).
- It creates a context that lets every descendant component know the current resource name (this context is called
ResourceContext
). - It stores the resource definition (its name, icon, and label) inside a shared context (this context is called
ResourceDefinitionContext
).
<Resource>
components can only be used as children of the <Admin>
component.
Usage
For instance, the following admin app offers an interface to the resources exposed by the JSONPlaceholder API (posts, users, comments, and tags):
import * as React from "react";
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
import { PostList, PostCreate, PostEdit, PostShow, PostIcon } from './posts';
import { UserList } from './posts';
import { CommentList, CommentEdit, CommentCreate, CommentIcon } from './comments';
const App = () => (
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}>
{/* complete CRUD pages for posts */}
<Resource name="posts" list={PostList} create={PostCreate} edit={PostEdit} show={PostShow} />
{/* read-only user list */}
<Resource name="users" list={UserList} />
{/* no show page for the comments resource */}
<Resource name="comments" list={CommentList} create={CommentCreate} edit={CommentEdit} icon={CommentIcon} />
</Admin>
);
The routes call the following dataProvider
methods:
list
callsgetList()
on mountshow
callsgetOne()
on mountedit
callsgetOne()
on mount, andupdate()
ordelete()
on submissioncreate
callscreate()
on submission
Tip: Which API endpoint does a resource rely on? The <Resource>
component doesnât know this mapping - itâs the dataProvider
âs job to define it.
name
name
is the only required prop for a <Resource>
. React-admin uses the name
prop both to determine the API endpoint (passed to the dataProvider
), and to form the URL for the resource.
<Resource name="posts" list={PostList} create={PostCreate} edit={PostEdit} show={PostShow} />
For this resource react-admin will fetch the https://jsonplaceholder.typicode.com/posts
endpoint for data.
The routing will map the component as follows:
/posts/
maps toPostList
/posts/create
maps toPostCreate
/posts/:id
maps toPostEdit
/posts/:id/show
maps toPostShow
Tip: If you want to use a special API endpoint (e.g. âhttps://jsonplaceholder.typicode.com/my-custom-posts-endpointâ) without altering the URL in the react-admin application (so still use /posts
), write the mapping from the resource name
(posts
) to the API endpoint (my-custom-posts-endpoint
) in your own dataProvider
.
list
, create
, edit
, show
<Resource>
allows you to define a component for each CRUD operation, using the following prop names:
list
(usually using the<List>
component) (if defined, the resource is displayed on the Menu)create
(usually using the<Create>
component)edit
(usually using the<Edit>
component)show
(usually using the<Show>
component)
Tip: Under the hood, the <Resource>
component uses react-router to create several routes:
/
maps to thelist
component/create
maps to thecreate
component/:id
maps to theedit
component/:id/show
maps to theshow
component
<Resource>
also accepts additional props:
children
<Resource>
defines the CRUD routes of your application. So <Resource name="posts">
defines a set of routes starting with /posts
.
<Resource>
accepts <Route>
components as children
, to let you define sub routes for the resource.
For instance, the following code creates an authors
resource, and adds an /authors/:authorId/books
route displaying the books of the given author:
// in src/App.jsx
import { Admin, Resource } from 'react-admin';
import { Route } from 'react-router-dom';
import { AuthorList } from './AuthorList';
import { BookList } from './BookList';
export const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="authors" list={AuthorList}>
<Route path=":authorId/books" element={<BookList />} />
</Resource>
</Admin>
);
The BookList
component can grab the authorId
parameter from the URL using the useParams
hook, and pass it as a <List filter>
parameter to display a list of books for the given author:
// in src/BookList.jsx
import { List, Datagrid, TextField } from 'react-admin';
import { useParams } from 'react-router-dom';
export const BookList = () => {
const { authorId } = useParams();
return (
<List resource="books" filter={{ authorId }}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="title" />
<TextField source="year" />
</Datagrid>
</List>
);
};
Tip: In the above example, the resource="books"
prop is required in <List>
because the ResourceContext
defaults to authors
inside the <Resource name="authors">
.
Itâs your responsibility to route to the /authors/:id/books
route, e.g. from each line of the AuthorList
component:
// in src/AuthorList.jsx
const BooksButton = () => {
const record = useRecordContext();
return (
<Button
component={Link}
to={`/authors/${record.id}/books`}
color="primary"
>
Books
</Button>
);
};
export const AuthorList = () => (
<List>
<Datagrid>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
<BooksButton />
</Datagrid>
</List>
);
Tip: As the /authors/:authorId/books
route is a sub-route of the /authors
route, the active menu item will be âAuthorsâ.
icon
React-admin will render the icon
prop component in the menu:
// in src/App.js
import * as React from "react";
import PostIcon from '@mui/icons-material/Book';
import UserIcon from '@mui/icons-material/People';
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
import { PostList } from './posts';
const App = () => (
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}>
<Resource name="posts" list={PostList} icon={PostIcon} />
<Resource name="users" list={UserList} icon={UserIcon} />
</Admin>
);
options
options.label
allows to customize the display name of a given resource in the menu.
<Resource name="v2/posts" options={{ label: 'Posts' }} list={PostList} />
recordRepresentation
Whenever react-admin needs to render a record (e.g. in the title of an edition view, or in a <ReferenceField>
), it uses the recordRepresentation
to do it. By default, the representation of a record is its id
field. But you can customize it by specifying the representation you want.
For instance, to change the default representation of âusersâ records to render the full name instead of the id:
<Resource
name="users"
list={UserList}
recordRepresentation={(record) => `${record.first_name} ${record.last_name}`}
/>
recordRepresentation
can take 3 types of values:
- a string (e.g.
'title'
) to specify the field to use as representation - a function (e.g.
(record) => record.title
) to specify a custom string representation - a React component (e.g.
<MyCustomRecordRepresentation />
). In such components, useuseRecordContext
to access the record.
Resource Context
<Resource>
also creates a ResourceContext
, that gives access to the current resource name to all descendants of the main page components (list
, create
, edit
, show
).
To read the current resource name, use the useResourceContext()
hook.
For instance, the following component displays the name of the current resource:
import * as React from 'react';
import { Datagrid, DateField, TextField, List, useResourceContext } from 'react-admin';
const ResourceName = () => {
const resource = useResourceContext();
return <>{resource}</>;
}
const PostList = () => (
<List>
<>
<ResourceName /> {/* renders 'posts' */}
<Datagrid>
<TextField source="title" />
<DateField source="published_at" />
</Datagrid>
</>
</List>
)
Tip: You can change the current resource context, e.g. to use a component for a related resource. Use the <ResourceContextProvider>
component for that:
const MyComponent = () => (
<ResourceContextProvider value="comments">
<ResourceName /> {/* renders 'comments' */}
...
</ResourceContextProvider>
);
Nested Resources
React-admin doesnât support nested resources, but you can use the children
prop to render a custom component for a given sub-route. For instance, to display a list of posts for a given user:
import { Admin, Resource } from 'react-admin';
import { Route } from 'react-router-dom';
export const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} edit={UserDetail}>
<Route path=":id/posts" element={<PostList />} />
<Route path=":id/posts/:postId" element={<PostDetail />} />
</Resource>
</Admin>
);
This setup creates four routes:
/users
renders the<UserList>
element/users/:id
renders the<UserDetail>
element/users/:id/posts
renders the<PostList>
element/users/:id/posts/:postId
renders the<PostDetail>
element
In order to display a list of posts for the selected user, <PostList>
should filter the posts by the id
parameter. To do so, use the useParams
hook from react-router-dom
:
// in src/PostList.jsx
import { List, Datagrid, TextField } from 'react-admin';
import { useParams } from 'react-router-dom';
export const PostList = () => {
const { id } = useParams();
return (
<List resource="posts" filter={{ userId: id }}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="title" />
<TextField source="year" />
</Datagrid>
</List>
);
};
In the <PostDetail>
component, you must also use the useParams
hook to get the postId
parameter and display the post with the corresponding id
:
// in src/PostDetail.jsx
import { Edit, SimpleForm, TextInput } from 'react-admin';
import { useParams } from 'react-router-dom';
export const PostDetail = () => {
const { postId } = useParams();
return (
<Edit resource="posts" id={postId}>
<SimpleForm>
<TextInput source="id" />
<TextInput source="title" />
<TextInput source="year" />
</SimpleForm>
</Edit>
);
};