Movatterモバイル変換


[0]ホーム

URL:


React-Admin Tutorial

In this 45-minute tutorial, you will learn how to create a web application with react-admin based on an existing REST API.

You can follow along by reading the text, or by watching the video below, made by Brad from Traversy Media, and which covers roughly the same content:

If you prefer text, you’re in good hands! We will go from absolute basics to a fully functional admin app. By the end of this tutorial, you will have a good understanding of the features and developer experience react-admin provides.

The final result is a web application that allows you to list, create, edit, and delete users and posts. Here is a preview of the app:

Setting Up

React-admin is built on React. To start, we’ll usecreate-react-admin to bootstrap a new web application:

npm init react-admin test-admin# oryarn create react-admin test-admin

When prompted, chooseJSON Server as the data provider, thenNone as the auth provider. Do not add any resources for now and pressEnter. Next, choose eithernpm oryarn and pressEnter. Once everything is installed, run the following commands:

cdtest-adminnpm run dev# oryarn dev

You should now see an empty React-admin application running on port 5173:

Empty Admin

Tip: Thecreate-react-admin script creates a single-page application powered byVite andTypeScript. You can also use JavaScript if you prefer. Additionally, react-admin works withNext.js,Remix, or any other React framework. React-admin is framework-agnostic.

Let’s explore the generated code. The main entry point isindex.tsx, which renders theApp component into the DOM:

// in src/index.tsximportReactfrom'react';importReactDOMfrom'react-dom/client';import{App}from'./App';ReactDOM.createRoot(document.getElementById('root')!).render(<React.StrictMode><App/></React.StrictMode>);

The<App> component renders the<Admin> component, which serves as the root of a react-admin application:

// in src/App.tsximport{Admin,Resource,ListGuesser,EditGuesser,ShowGuesser}from'react-admin';import{dataProvider}from'./dataProvider';exportconstApp=()=>(<AdmindataProvider={dataProvider}></Admin>);

Right now, this component only defines adataProvider prop. But what exactly is a data provider?

Using an API as the Data Source

React-admin apps are single-page applications (SPAs) that run in the browser and fetch data from an API. Since there is no single standard for data exchanges between systems, react-admin uses an adapter to communicate with your API—this adapter is called aData Provider.

For this tutorial, we’ll useJSONPlaceholder, a fake REST API designed for prototyping and testing. Here is a sample response:

curl https://jsonplaceholder.typicode.com/users/2
{"id":2,"name":"Ervin Howell","username":"Antonette","email":"Shanna@melissa.tv","address":{"street":"Victor Plains","suite":"Suite 879","city":"Wisokyburgh","zipcode":"90566-7771","geo":{"lat":"-43.9509","lng":"-34.4618"}},"phone":"010-692-6593 x09125","website":"anastasia.net","company":{"name":"Deckow-Crist","catchPhrase":"Proactive didactic contingency","bs":"synergize scalable supply-chains"}}

JSONPlaceholder provides endpoints for users, posts, and comments. The admin app we’ll build will allow you to Create, Retrieve, Update, and Delete (CRUD) these resources.

Thetest-admin project you created already contains a pre-configured data provider for JSONPlaceholder:

// in src/dataProvider.tsimportjsonServerProviderfrom'ra-data-json-server';exportconstdataProvider=jsonServerProvider(import.meta.env.VITE_JSON_SERVER_URL);

This uses a third-party package,ra-data-json-server, which maps the JSONPlaceholder API to the react-admin CRUD API. There aredozens of data provider packages for various APIs and databases. You can also create your own if necessary. For now, let’s make sure the app connects to JSONPlaceholder.

Tip: Theimport.meta.env.VITE_JSON_SERVER_URL expression is aVite environment variable, which is set tohttps://jsonplaceholder.typicode.com in the.env file located at the project root.

Mapping API Endpoints with Resources

Next, let’s add a list of users.

The<Admin> component expects one or more<Resource> child components. Each resource maps a name to an API endpoint. To add a resource namedusers, edit theApp.tsx file as follows:

// in src/App.tsximport { Admin, Resource, ListGuesser, EditGuesser, ShowGuesser } from 'react-admin';import { dataProvider } from './dataProvider';export const App = () => (  <Admin dataProvider={dataProvider}>+   <Resource name="users" list={ListGuesser} />  </Admin>);

The<Resource name="users" /> line instructs react-admin to fetch “users” from thehttps://jsonplaceholder.typicode.com/users URL. The<Resource> component also defines which React components to use for each CRUD operation (list,create,edit, andshow).

list={ListGuesser} tells react-admin to use the<ListGuesser> component to display the list of users. This componentguesses the configuration for the list, including column names and types, based on the data fetched from the API.

Now, your app can display a list of users:

Users List

The list is already functional: you can sort it by clicking on the column headers or navigate through pages using the pagination controls. If you open the network tab in your browser’s developer tools, you’ll see that every user action on the list triggers a corresponding HTTP request tohttps://jsonplaceholder.typicode.com/users with updated parameters. The data provider handles these requests, translating user actions into API calls that the backend understands.

Writing a Page Component

The<ListGuesser> component isn’t meant for production use—it’s just there to help you quickly set up an admin interface. Eventually, you’ll need to replace theListGuesser in theusers resource with a custom React component. Fortunately,ListGuesser provides the guessed list code right in the console:

Guessed Users List

Copy this code and create a newUserList component in a new file calledusers.tsx:

// in src/users.tsximport{List,Datagrid,TextField,EmailField}from"react-admin";exportconstUserList=()=>(<List><Datagrid><TextFieldsource="id"/><TextFieldsource="name"/><TextFieldsource="username"/><EmailFieldsource="email"/><TextFieldsource="address.street"/><TextFieldsource="phone"/><TextFieldsource="website"/><TextFieldsource="company.name"/></Datagrid></List>);

Next, updateApp.tsx to use this new component instead ofListGuesser:

// in src/App.tsx-import { Admin, Resource, ListGuesser, EditGuesser, ShowGuesser } from 'react-admin';+import { Admin, Resource } from "react-admin";import { dataProvider } from './dataProvider';+import { UserList } from "./users";export const App = () => (  <Admin dataProvider={dataProvider}>-   <Resource name="users" list={ListGuesser} />+   <Resource name="users" list={UserList} />  </Admin>);

Users List

Visually, nothing changes in the browser, but now the app uses a component that you can fully customize.

Composing Components

Let’s take a closer look at the<UserList> component:

exportconstUserList=()=>(<List><Datagrid><TextFieldsource="id"/><TextFieldsource="name"/><TextFieldsource="username"/><EmailFieldsource="email"/><TextFieldsource="address.street"/><TextFieldsource="phone"/><TextFieldsource="website"/><TextFieldsource="company.name"/></Datagrid></List>);

The root component,<List>, reads the query parameters, fetches data from the API, and places the data in a React context. It also provides callbacks for filtering, pagination, and sorting, allowing child components to access and modify the list parameters.<List> performs many tasks, but its syntax remains straightforward:

<List>{/* children */}</List>

This demonstrates the goal of react-admin: helping developers build sophisticated applications with simple syntax.

In most frameworks, “simple” often implies limited capabilities, making it challenging to extend beyond basic features. React-admin addresses this throughcomposition.<List> handles data fetching, while rendering is delegated to its child—in this case,<Datagrid>. Essentially, the code composes the functionalities of<List> and<Datagrid> functionalities.

This means we can compose<List> with another component - for instance<SimpleList>:

// in src/users.tsximport{List,SimpleList}from"react-admin";exportconstUserList=()=>(<List><SimpleListprimaryText={(record)=>record.name}secondaryText={(record)=>record.username}tertiaryText={(record)=>record.email}/></List>);

<SimpleList> usesMaterial UI’s<List> and<ListItem> components and expects functions forprimaryText,secondaryText, andtertiaryText props.

Refresh the page, and you’ll see the list rendered differently:

Users List

React-admin offers a wide range of components to help you build your UI. You can also create your own components if needed.

Writing a Custom List Component

React-admin’s layout is responsive by default. Try resizing your browser, and you’ll notice the sidebar turns into a drawer on smaller screens. The<SimpleList> component works well for mobile devices.

However,<SimpleList> has low information density on desktop. Let’s modify<UserList> to use<Datagrid> on larger screens and<SimpleList> on smaller screens. We can achieve this usingMaterial UI’suseMediaQuery hook:

// in src/users.tsximport{useMediaQuery,Theme}from"@mui/material";import{List,SimpleList,Datagrid,TextField,EmailField}from"react-admin";exportconstUserList=()=>{constisSmall=useMediaQuery<Theme>((theme)=>theme.breakpoints.down("sm"));return(<List>{isSmall?(<SimpleListprimaryText={(record)=>record.name}secondaryText={(record)=>record.username}tertiaryText={(record)=>record.email}/>):(<Datagrid><TextFieldsource="id"/><TextFieldsource="name"/><TextFieldsource="username"/><EmailFieldsource="email"/><TextFieldsource="address.street"/><TextFieldsource="phone"/><TextFieldsource="website"/><TextFieldsource="company.name"/></Datagrid>)}</List>);};

This works exactly as you’d expect.

The<List> component’s child can be anything—even a custom component with its own logic. This flexibility makes react-admin ideal for building responsive apps.

Selecting Columns

Let’s get back to<Datagrid>. It reads the data fetched by<List>, then renders a table with one row for each record.<Datagrid> uses its child components (here, a list ofField component) to render the columns. Each Field component renders one field of the current record, specified by thesource prop.

<ListGuesser> created one column for every field in the API response. That’s a bit too much for a usable grid, so let’s remove a couple of<TextField> components from the Datagrid and see the effect:

// in src/users.tsx  <Datagrid>    <TextField source="id" />    <TextField source="name" />-   <TextField source="username" />    <EmailField source="email" />-   <TextField source="address.street" />    <TextField source="phone" />    <TextField source="website" />    <TextField source="company.name" />  </Datagrid>

Users List

In react-admin, most configuration is done through components. Instead of using acolumns prop for configuration, react-admin leverages thechildren prop for flexibility, enabling you to add custom logic or change column types as needed.

Using Field Types

So far, you’ve used<TextField> and<EmailField>. React-admin providesmany more Field components to handle different data types—numbers, dates, images, arrays, and more.

For instance, instead of displaying thewebsite field as plain text, you could make it a clickable link using<UrlField>:

// in src/users.tsx-import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin";+import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin";// ...  <Datagrid>    <TextField source="id" />    <TextField source="name" />    <EmailField source="email" />    <TextField source="phone" />-   <TextField source="website" />+   <UrlField source="website" />    <TextField source="company.name" />  </Datagrid>

Url Field

This is typical of the early stages of development with react-admin: use a guesser component to bootstrap the basic page, then adjust the code to better fit your business needs.

Writing A Custom Field

In react-admin, fields are just React components. When rendered, they grab therecord fetched from the API (e.g.{ "id": 2, "name": "Ervin Howell", "website": "anastasia.net", ... }) using a custom hook, and use thesource prop (e.g.website) to get the value they should display (e.g. “anastasia.net”).

That means you can do the same towrite a custom field. For instance, here is a simplified version of the<UrlField>:

// in src/MyUrlField.tsximport{useRecordContext}from"react-admin";constMyUrlField=({source}:{source:string})=>{constrecord=useRecordContext();if(!record)returnnull;return<ahref={`http://${record[source]}`}>{record[source]}</a>;};exportdefaultMyUrlField;

For each row,<Datagrid> creates aRecordContext and stores the current record in it.useRecordContext allows you to read that record. It’s one of the 50+ headless hooks that react-admin exposes to let you build your own components without forcing a particular UI.

You can use the<MyUrlField> component in<UserList> instead of react-admin’s<UrlField> component, and it will work just the same.

// in src/users.tsx-import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin";+import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin";+import MyUrlField from './MyUrlField';// ...  <Datagrid>    <TextField source="id" />    <TextField source="name" />    <EmailField source="email" />    <TextField source="phone" />-   <UrlField source="website" />+   <MyUrlField source="website" />    <TextField source="company.name" />  </Datagrid>

This means react-admin never blocks you: if one react-admin component doesn’t perfectly suit your needs, you can just swap it with your own version.

Customizing Styles

The<MyUrlField> component is a perfect opportunity to illustrate how to customize styles.

React-admin relies onMaterial UI, a set of React components modeled after Google’sMaterial Design Guidelines. All Material UI components (and most react-admin components) support a prop calledsx, which allows custom inline styles. Let’s take advantage of thesx prop to remove the underline from the link and add an icon:

// in src/MyUrlField.tsximport{useRecordContext}from"react-admin";import{Link}from"@mui/material";importLaunchIconfrom"@mui/icons-material/Launch";constMyUrlField=({source}:{source:string})=>{constrecord=useRecordContext();returnrecord?(<Linkhref={record[source]}sx={{textDecoration:"none"}}>{record[source]}<LaunchIconsx={{fontSize:15,ml:1}}/></Link>):null;};exportdefaultMyUrlField;

Custom styles

Thesx prop is like React’sstyle prop, except it supports theming, media queries, shorthand properties, and much more. It’s a CSS-in-JS solution, so you’ll have to use the JS variants of the CSS property names (e.g.textDecoration instead oftext-decoration).

Tip: There is much more to Material UI styles than what this tutorial covers. Read theTheming documentation to learn more about theming, vendor prefixes, responsive utilities, etc.

Tip: Material UI supports other CSS-in-JS solutions, includingStyled components.

Handling Relationships

In JSONPlaceholder, eachpost record includes auserId field, which points to auser:

{"id":1,"title":"sunt aut facere repellat provident occaecati excepturi optio reprehenderit","body":"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto","userId":1}

React-admin knows how to take advantage of these foreign keys to fetch references. Let’s see how theListGuesser manages them by creating a new<Resource> for the/posts API endpoint:

// in src/App.tsx-import { Admin, Resource } from "react-admin";+import { Admin, Resource, ListGuesser } from "react-admin";import { dataProvider } from './dataProvider';import { UserList } from "./users";export const App = () => (  <Admin dataProvider={dataProvider}>+   <Resource name="posts" list={ListGuesser} />    <Resource name="users" list={UserList} />  </Admin>);

Guessed Post List

TheListGuesser suggests using a<ReferenceField> for theuserId field. Let’s play with this new field by creating thePostList component based on the code dumped by the guesser:

// in src/posts.tsximport{List,Datagrid,TextField,ReferenceField}from"react-admin";exportconstPostList=()=>(<List><Datagrid><ReferenceFieldsource="userId"reference="users"/><TextFieldsource="id"/><TextFieldsource="title"/><TextFieldsource="body"/></Datagrid></List>);
// in src/App.tsx-import { Admin, Resource, ListGuesser } from "react-admin";+import { Admin, Resource } from "react-admin";import { dataProvider } from './dataProvider';+import { PostList } from "./posts";import { UserList } from "./users";export const App = () => (    <Admin dataProvider={dataProvider}>-       <Resource name="posts" list={ListGuesser} />+       <Resource name="posts" list={PostList} />        <Resource name="users" list={UserList} />    </Admin>);

When displaying the posts list, react-admin is smart enough to display thename of the post author:

Post List With User Names

Tip: To customize how to represent a record, settherecordRepresentation prop of the<Resource>.

The<ReferenceField> component fetches the reference data, creates aRecordContext with the result, and renders the record representation (or its children).

Tip: Look at the network tab of your browser again: react-admin deduplicates requests for users and aggregates them in order to make onlyone HTTP request to the/users endpoint for the whole Datagrid. That’s one of many optimizations that keep the UI fast and responsive.

To finish the post list, place the postid field as the first column, and remove thebody field. From a UX point of view, fields containing large chunks of text should not appear in a Datagrid, only in detail views. Also, to make the Edit action stand out, let’s replace the defaultrowClick action with an explicit action button:

// in src/posts.tsx-import { List, Datagrid, TextField, ReferenceField } from "react-admin";+import { List, Datagrid, TextField, ReferenceField, EditButton } from "react-admin";export const PostList = () => (  <List>-   <Datagrid>+   <Datagrid rowClick={false}>+     <TextField source="id" />      <ReferenceField source="userId" reference="users" />-     <TextField source="id" />      <TextField source="title" />-     <TextField source="body" />+     <EditButton />    </Datagrid>  </List>);

Post List With Fewer Columns

Adding A Detail View

So far, the admin only has list pages. Additionally, the user list doesn’t render all columns, so you need to add a detail view to see all the user fields. The<Resource> component accepts ashow component prop to define a detail view. Let’s use the<ShowGuesser> to help bootstrap it:

// in src/App.tsx-import { Admin, Resource } from "react-admin";+import { Admin, Resource, ShowGuesser } from "react-admin";import { dataProvider } from './dataProvider';import { PostList } from "./posts";import { UserList } from "./users";export const App = () => (    <Admin dataProvider={dataProvider}>        <Resource name="posts" list={PostList} />-       <Resource name="users" list={UserList}  />+       <Resource name="users" list={UserList} show={ShowGuesser}  />    </Admin>);

Now you can click on a user in the list to see their details:

Just like for other guessed components, you can customize the show view by copying the code dumped by the<ShowGuesser> and modifying it to suit your needs. This is out of scope for this tutorial, so we’ll leave it as is.

Now that theusers resource has ashow view, you can also link to it from the post list view. To do this, edit the<ReferenceField> component to addlink="show", as follows:

// in src/posts.tsxexport const PostList = () => (    <List>        <Datagrid>-           <ReferenceField source="userId" reference="users" />+           <ReferenceField source="userId" reference="users" link="show" />            <TextField source="id" />            <TextField source="title" />            <TextField source="body" />        </Datagrid>    </List>);

Post List With User Links

Reference components let users navigate from one resource to another naturally. They are a key feature of react-admin.

Adding Editing Capabilities

An admin interface isn’t just about displaying remote data; it should also allow editing records. React-admin provides an<Edit> component for this purpose. Let’s use the<EditGuesser> to help bootstrap it.

// in src/App.tsx-import { Admin, Resource, ShowGuesser } from "react-admin";+import { Admin, Resource, ShowGuesser, EditGuesser } from "react-admin";import { dataProvider } from './dataProvider';import { PostList } from "./posts";import { UserList } from "./users";export const App = () => (    <Admin dataProvider={dataProvider}>-       <Resource name="posts" list={PostList} />+       <Resource name="posts" list={PostList} edit={EditGuesser} />        <Resource name="users" list={UserList} show={ShowGuesser}  />    </Admin>);

Users can display the edit page just by clicking on the Edit button. The form is already functional; it issuesPUT requests to the REST API upon submission. Thanks to therecordRepresentation of the “users” resource, the user name is displayed for the post author.

Copy the<PostEdit> code dumped by the guesser in the console to theposts.tsx file so that you can customize the view:

// in src/posts.tsximport{List,Datagrid,TextField,ReferenceField,EditButton,Edit,SimpleForm,ReferenceInput,TextInput,}from"react-admin";exportconstPostList=()=>{/* ... */};exportconstPostEdit=()=>(<Edit><SimpleForm><ReferenceInputsource="userId"reference="users"/><TextInputsource="id"/><TextInputsource="title"/><TextInputsource="body"/></SimpleForm></Edit>);

Use that component as theedit prop of the “posts” resource instead of the guesser:

// in src/App.tsx-import { Admin, Resource, ShowGuesser, EditGuesser } from "react-admin";+import { Admin, Resource, ShowGuesser } from "react-admin";import { dataProvider } from './dataProvider';-import { PostList } from "./posts";+import { PostList, PostEdit } from "./posts";import { UserList } from "./users";export const App = () => (  <Admin dataProvider={dataProvider}>-   <Resource name="posts" list={PostList} edit={EditGuesser} />+   <Resource name="posts" list={PostList} edit={PostEdit} />    <Resource name="users" list={UserList} show={ShowGuesser}  />  </Admin>);

You can now adjust the<PostEdit> component to disable editing of the primary key (id), place it first, and use a textarea for thebody field, as follows:

// in src/posts.tsxexport const PostEdit = () => (  <Edit>    <SimpleForm>+     <TextInput source="id" InputProps={{ disabled: true }} />      <ReferenceInput source="userId" reference="users" link="show" />-     <TextInput source="id" />      <TextInput source="title" />-     <TextInput source="body" />+     <TextInput source="body" multiline rows={5} />    </SimpleForm>  </Edit>);

If you’ve understood the<List> component, the<Edit> component will be no surprise. It’s responsible for fetching the record and displaying the page title. It passes the record down to the<SimpleForm> component, which is responsible for the form layout, default values, and validation. Just like<Datagrid>,<SimpleForm> uses its children to determine the form inputs to display. It expectsinput components as children.<TextInput> and<ReferenceInput> are such inputs.

The<ReferenceInput> takes the same props as the<ReferenceField> (used earlier in the<PostList> page).<ReferenceInput> uses these props to fetch the API for possible references related to the current record (in this case, possibleusers for the currentpost). It then creates a context with the possible choices and renders an<AutocompleteInput>, which is responsible for displaying the choices and letting the user select one.

Adding Creation Capabilities

Let’s allow users to create posts, too. Copy the<PostEdit> component into a<PostCreate>, and replace<Edit> with<Create>:

// in src/posts.tsximport {    List,    Datagrid,    TextField,    ReferenceField,    EditButton,    Edit,+   Create,    SimpleForm,    ReferenceInput,    TextInput,} from "react-admin";export const PostList = () => (  { /* ... */ });export const PostEdit = () => (  { /* ... */ });+export const PostCreate = () => (+  <Create>+    <SimpleForm>+      <ReferenceInput source="userId" reference="users" />+      <TextInput source="title" />+      <TextInput source="body" multiline rows={5} />+    </SimpleForm>+  </Create>+);

Tip: The<PostEdit> and the<PostCreate> components use almost the same child form, except for the additionalid input in<PostEdit>. In most cases, the forms for creating and editing a record are a bit different, because most APIs create primary keys server-side. But if the forms are the same, you can share a common form component in<PostEdit> and<PostCreate>.

To use the new<PostCreate> component in the posts resource, just add it as thecreate attribute in the<Resource name="posts"> component:

// in src/App.tsximport { Admin, Resource, ShowGuesser } from "react-admin";import { dataProvider } from './dataProvider';-import { PostList, PostEdit } from "./posts";+import { PostList, PostEdit, PostCreate } from "./posts";import { UserList } from "./users";export const App = () => (  <Admin dataProvider={dataProvider}>-   <Resource name="posts" list={PostList} edit={PostEdit} />+   <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} />    <Resource name="users" list={UserList} show={ShowGuesser}  />  </Admin>);

React-admin automatically adds a “create” button on top of the posts list to give access to thecreate component. And the creation form works; it issues aPOST request to the REST API upon submission.

Optimistic Rendering And Undo

Unfortunately, JSONPlaceholder is a read-only API; although it seems to acceptPOST andPUT requests, it doesn’t take into account the creations and edits - that’s why, in this particular case, you will see errors after creation, and you won’t see your edits after you save them. It’s just an artifact of JSONPlaceholder.

But then, how come the newly created post appears in the list just after creation in the screencast above?

That’s because react-admin usesoptimistic updates. When a user edits a record and hits the “Save” button, the UI shows a confirmation and displays the updated databefore sending the update query to the server. The main benefit is that UI changes are immediate—no need to wait for the server response. It’s a great comfort for users.

But there is an additional benefit: it also allows the “Undo” feature. Undo is already functional in the admin at this point. Try editing a record, then hit the “Undo” link in the black confirmation box before it slides out. You’ll see that the app does not send theUPDATE query to the API and displays the non-modified data.

Even though updates appear immediately due to optimistic rendering, react-admin only sends them to the server after a short delay (about 5 seconds). During this delay, the user can undo the action, and react-admin will never send the update.

Optimistic updates and undo require no specific code on the API side—react-admin handles them purely on the client side. That means you’ll get them for free with your own API!

Note: When you add the ability to edit an item, you also add the ability to delete it. The “Delete” button in the edit view is fully functional out of the box-and it is also “undoable”.

Adding Search And Filters To The List

Let’s get back to the post list for a minute. It offers sorting and pagination, but one feature is missing: the ability to search content.

React-admin can use input components to create a multi-criteria search engine in the list view. Pass an array of such input components to the Listfilters prop to enable filtering:

// in src/posts.tsxconstpostFilters=[<TextInputsource="q"label="Search"alwaysOn/>,<ReferenceInputsource="userId"label="User"reference="users"/>,];exportconstPostList=()=>(<Listfilters={postFilters}>        // ...</List>);

The first filter, ‘q’, takes advantage of a full-text functionality offered by JSONPlaceholder. It isalwaysOn, so it always appears on the screen. Users can add the second filter,userId, using the “add filter” button located on the top of the list. Since it’s a<ReferenceInput>, it’s already populated with possible users.

Filters are “search-as-you-type”, meaning that when the user enters new values in the filter form, the list refreshes (via an API request) immediately.

Tip: Thelabel property can be used on any input to customize its label.

Customizing the Menu Icons

The sidebar menu shows the same icon for both posts and users. Customizing the menu icon is just a matter of passing anicon attribute to each<Resource>:

// in src/App.tsximportPostIconfrom"@mui/icons-material/Book";importUserIconfrom"@mui/icons-material/Group";exportconstApp=()=>(<AdmindataProvider={dataProvider}><Resourcename="posts"list={PostList}edit={PostEdit}create={PostCreate}icon={PostIcon}/><Resourcename="users"list={UserList}show={ShowGuesser}icon={UserIcon}/></Admin>);

Using a Custom Home Page

By default, react-admin displays the list page of the firstResource element as the home page. If you want to display a custom component instead, pass it in thedashboard prop of the<Admin> component.

// in src/Dashboard.tsximport{Card,CardContent,CardHeader}from"@mui/material";exportconstDashboard=()=>(<Card><CardHeadertitle="Welcome to the administration"/><CardContent>Lorem ipsum sic dolor amet...</CardContent></Card>);
// in src/App.tsximport{Dashboard}from'./Dashboard';exportconstApp=()=>(<AdmindataProvider={dataProvider}dashboard={Dashboard}>          // ...</Admin>);

Custom home page

Adding Authentication

Most admin apps require authentication. React-admin can check user credentials before displaying a page and redirect to a login page when the REST API returns a 403 error code.

React-admin makes no assumption about your authentication strategy (basic auth, OAuth, custom route, etc.), but gives you the ability to add the auth logic at the right place - usingtheauthProvider object.

For this tutorial, since there is no public authentication API, we can use a fake authentication provider that accepts every login request and stores theusername inlocalStorage. Each page change will require thatlocalStorage contains ausername item.

TheauthProvider must expose 4 async methods:

// in src/authProvider.tsimport{AuthProvider}from"react-admin";exportconstauthProvider:AuthProvider={// called when the user attempts to log inasynclogin({username,password}){// accept all username/password combinationsif(false){thrownewError("Invalid credentials, please try again");}localStorage.setItem("username",username);},// called when the user clicks on the logout buttonasynclogout(){localStorage.removeItem("username");},// called when the API returns an errorasynccheckError({status}:{status:number}){if(status===401||status===403){localStorage.removeItem("username");thrownewError("Session expired");}},// called when the user navigates to a new location, to check for authenticationasynccheckAuth(){if(!localStorage.getItem("username")){thrownewError("Authentication required");}},};

To enable this authentication strategy, pass theauthProvider to the<Admin> component:

// in src/App.tsximport{Dashboard}from'./Dashboard';import{authProvider}from'./authProvider';exportconstApp=()=>(<AdminauthProvider={authProvider}dataProvider={dataProvider}dashboard={Dashboard}>        // ...</Admin>);

Once the app reloads, it’s now behind a login form that accepts everyone:

Connecting To A Real API

Here is the elephant in the room of this tutorial. In real-world projects, the dialect of your API (REST? GraphQL? Something else?) won’t match the JSONPlaceholder dialect.Writing a Data Provider is probably the first thing you’ll have to do to make react-admin work, unless your API backend is already supported (see the list here). Depending on your API, this can require a few hours of additional work.

React-admin delegates every data query to a Data Provider object, which acts as an adapter to your API. This makes react-admin capable of mapping any API dialect, using endpoints from several domains, etc.

For instance, let’s imagine you have to use themy.api.url REST API, which expects the following parameters:

ActionExpected API request
Get listGET http://my.api.url/posts?sort=["title","ASC"]&range=[0, 24]&filter={"title":"bar"}
Get one recordGET http://my.api.url/posts/123
Get several recordsGET http://my.api.url/posts?filter={"id":[123,456,789]}
Get related recordsGET http://my.api.url/posts?filter={"author_id":345}
Create a recordPOST http://my.api.url/posts
Update a recordPUT http://my.api.url/posts/123
Update recordsPUT http://my.api.url/posts?filter={"id":[123,124,125]}
Delete a recordDELETE http://my.api.url/posts/123
Delete recordsDELETE http://my.api.url/posts?filter={"id":[123,124,125]}

React-admin calls the Data Provider with one method for each of the actions on this list and expects a Promise in return. These methods are calledgetList,getOne,getMany,getManyReference,create,update,updateMany,delete, anddeleteMany. It’s the Data Provider’s job to emit HTTP requests and transform the response into the format expected by react-admin.

The code for a Data Provider for themy.api.url API is as follows:

// in src/dataProvider.tsimport{DataProvider,fetchUtils}from"react-admin";import{stringify}from"query-string";constapiUrl='https://my.api.com/';consthttpClient=fetchUtils.fetchJson;exportconstdataProvider:DataProvider={getList:(resource,params)=>{const{page,perPage}=params.pagination;const{field,order}=params.sort;constquery={sort:JSON.stringify([field,order]),range:JSON.stringify([(page-1)*perPage,page*perPage-1]),filter:JSON.stringify(params.filter),};consturl=`${apiUrl}/${resource}?${stringify(query)}`;returnhttpClient(url).then(({headers,json})=>({data:json,total:parseInt((headers.get('content-range')||"0").split('/').pop()||'0',10),}));},getOne:(resource,params)=>httpClient(`${apiUrl}/${resource}/${params.id}`).then(({json})=>({data:json,})),getMany:(resource,params)=>{constquery={filter:JSON.stringify({id:params.ids}),};consturl=`${apiUrl}/${resource}?${stringify(query)}`;returnhttpClient(url).then(({json})=>({data:json}));},getManyReference:(resource,params)=>{const{page,perPage}=params.pagination;const{field,order}=params.sort;constquery={sort:JSON.stringify([field,order]),range:JSON.stringify([(page-1)*perPage,page*perPage-1]),filter:JSON.stringify({...params.filter,[params.target]:params.id,}),};consturl=`${apiUrl}/${resource}?${stringify(query)}`;returnhttpClient(url).then(({headers,json})=>({data:json,total:parseInt((headers.get('content-range')||"0").split('/').pop()||'0',10),}));},update:(resource,params)=>httpClient(`${apiUrl}/${resource}/${params.id}`,{method:'PUT',body:JSON.stringify(params.data),}).then(({json})=>({data:json})),updateMany:(resource,params)=>{constquery={filter:JSON.stringify({id:params.ids}),};returnhttpClient(`${apiUrl}/${resource}?${stringify(query)}`,{method:'PUT',body:JSON.stringify(params.data),}).then(({json})=>({data:json}));},create:(resource,params)=>httpClient(`${apiUrl}/${resource}`,{method:'POST',body:JSON.stringify(params.data),}).then(({json})=>({data:{...params.data,id:json.id}asany,})),delete:(resource,params)=>httpClient(`${apiUrl}/${resource}/${params.id}`,{method:'DELETE',}).then(({json})=>({data:json})),deleteMany:(resource,params)=>{constquery={filter:JSON.stringify({id:params.ids}),};returnhttpClient(`${apiUrl}/${resource}?${stringify(query)}`,{method:'DELETE',}).then(({json})=>({data:json}));}};

Tip:fetchUtils.fetchJson() is just a shortcut forfetch().then(r => r.json()), plus control of the HTTP response code to throw anHTTPError in case of a 4xx or 5xx response. Feel free to usefetch() directly if it doesn’t suit your needs.

Using this provider instead of the previousjsonServerProvider is just a matter of switching a function:

// in src/app.tsximport{dataProvider}from'./dataProvider';constApp=()=>(<AdmindataProvider={dataProvider}>    // ...</Admin>);

Conclusion

React-admin was built with customization in mind. You can replace any react-admin component with a component of your own, for instance, to display a custom list layout or a different edit form for a given resource.

Now that you’ve completed the tutorial, continue your journey with theGuides and Concepts section.

Tip: React-admin is a large framework, so its documentation is quite extensive. Don’t get intimidated! React-admin works well for projects of any size. To continue your discovery of react-admin, we recommend that youenable beginner mode by clicking on the button located at the top of the navigation sidebar. It will hide the advanced features from the sidebar. Just remember to disable it when you’re ready to go further.

And to help you close the gap between theoretical knowledge and practical experience, take advantage of the react-adminDemos. They are great examples of how to use react-admin in a real-world application. They also show the best practices for going beyond simple CRUD apps.


[8]ページ先頭

©2009-2025 Movatter.jp