+ REACT
https://blog.logrocket.com/create-react-admin-panel/
How to create a React admin panel
Editor’s note: This post was last updated on 12 August 2021. It may still contain information that is out of date.
A good number of web applications have evolved from static websites that just display content to dynamic webpages where users access and interact with content. This content is often powered by APIs that send and receive data.
Often, an admin page sorts and handles this data, usually by building an interface and connecting every endpoint by sending requests to the API. This was previously a tedious process until react-admin
was introduced.
In this blog post, we will learn how react-admin
scaffolds admin interfaces for React applications.
What is react-admin
?
react-admin
is a framework that builds admin interfaces by consuming APIs, including RESET, GraphQL, or custom. We also don’t need to worry about style formatting because react-admin
is themed with Material-UI, a React library used to design application interfaces.
Getting started with react-admin
Let’s begin by creating a new-react-admin
folder and installing package.json
, concurrently
, and json-server
in a new server
folder:
># install package.json, concurrently & json-server
npm init -y
npm install json-server
npm install concurrently
Instead of installing json-server
globally, we’ll create an npm script for it. Open up the package.json
file and replace the default value of the scripts
object:
>// /new-react-admin/server/package.json
# - "test": "echo \"Error: no test specified \" && exit 1"
+ "server": "json-server --watch db.json --port 5000",
+ "client": "npm start --prefix admin-demo",
+ "dev": "concurrently \"npm run server\" \"npm run client\""
# "admin-demo" will be the name of our react app
In the code block above, we want to run json-server
and watch a file called db.json
, which holds the data generated by the fake REST API we’ll be using. By default, this runs on port 3000, which prevents our React app from running. Instead, we’ll set it to port 5000.
Concurrently is a framework that enables the API and React app to run simultaneously instead of running in separate terminals. As shown above, the client
script is assigned to start the React app and the dev
script runs both the server
and client
scripts simultaneously.
Now, we can create a React project in a new folder called admin-demo
and install react-admin
in its directory:
>npx create-react-app admin-demo
cd admin-demo
# install react-admin
npm install react-admin
Then, add a proxy
to the package.json
file of the React app; the proxy
is set to the server
script url:
>// /new-react-admin/admin-demo/package.json
"proxy": "http://localhost:5000"
Next, we’ll need to fill the db.json
file with some data. Here’s a sample of the data we’ll use:
>// /new-react-admin/server/db.json
{
"users": [
{
"id": "1",
"name": "Klaus Huntelaar",
"username": "Kanaar",
"email": "applboss@gmail.com",
"phone": 37802223,
"company": "Hunters Mobile Co."
},
{
"id": "2",
"name": "Peggy Sonaya",
"username": "Peggaya",
"email": "sonayapeg@gmail.com",
"phone": 4402673,
"company": "Peggy's Drycleaning"
},
{
"id": "3",
"name": "Joseph Maguire",
"username": "Joemag",
"email": "Jojo1956@gmail.com",
"phone": 224423045,
"company": "Jojo's Pot"
},
{
"id": "4",
"name": "Jon Hanus",
"username": "Hanuson",
"email": "Joehanus11@gmail.com",
"phone": 89354033,
"company": "Joe's Funeral Home"
},
{
"id": "5",
"name": "Ron Burgundy",
"username": "Colorburgundy",
"email": "Rongundy@gmail.com",
"phone": 23455,
"company": "Color Burgundy"
}
]
}
Modifying and filtering data with guessers
It is essential for an admin page to have CRUD functionalities. We’ll use a data provider to show how react-admin
does this. Simple REST is a data provider that fits REST APIs using simple GET parameters, which exist for filters and sorting.
We first need to install simple REST in our react-admin
app:
>// /new-react-admin/admin-demo
npm install ra-data-simple-rest
Let’s try fetching data with db.json
. For this purpose, react-admin
uses <Admin>
, its root component, to provide the data exchange between APIs and applications. Replace the default syntax in src/App.js
with the following:
>import React from 'react'
import { Admin} from 'react-admin'
import restProvider from 'ra-data-simple-rest'
const dataProvider = restProvider('http://localhost:3000');
function App() {
return (
<Admin dataProvider={dataProvider} />
);
}
export default App;
Now, changing directories from the React app to the main folder, cd ..
, and running npm
dev
at this point should render an app with a confirmatory message in a browser:
To display the data from the db.json
file, the API server needs to have a content range value to prevent it from throwing an error. With the help of middleware, we’ll create a file called range.js
that has a function to set the amount of content to display:
>// /new-react-admin/server/range.js
module.exports = (req, res, next) => {
res.header('Content-Range', 'posts 0-20/20')
next()
}
For the middleware function to run, we must add --middlewares ./range.js
to the server
script in the package.json
file.
How to use guessers in React
While still in development, react-admin
creates admin interfaces through “guessers.” Guessers receive data from the API, determine what sort of data it is, and make decisions on what kind of format the data should display in.
Let’s try to display a list of users by applying guessers:
>import React from 'react'
import { Admin, Resource,ListGuesser } from 'react-admin'
import restProvider from 'ra-data-simple-rest'
const dataProvider = restProvider('http://localhost:3000');
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={ListGuesser} />
</Admin>
);
}
export default App;
In the code block above, the <resource>
element is responsible for mapping the name
property to an endpoint in the API. Here <resource>
appends the users
value to our API and fetches the data of users from the API.
Using the <ListGuesser>
component
The list
property uses the <ListGuesser>
component to display this data as a list of users.
<ListGuesser>
is not meant to be used in production, thus it must be replaced by a custom component. An awesome feature of guessers is the display of the source code of data retrieved from the API in the browser’s console.
Let’s take a look at what <ListGuesser>
displays:
So this shows us how our user list should be created. Let’s replicate this data in our application. In the src
folder of the project, create a file and name it User.js
:
>/src/components/User.js
import React from 'react';
import { List, Datagrid, TextField, EmailField, EditButton, DeleteButton } from 'react-admin';
export const UserList = (props) => {
return (
<List {...props}>
<Datagrid>
<TextField source="id" />
<TextField source="name" />
<TextField source="username" />
<EmailField source="email" />
<TextField source="phone" />
<TextField source="company" label="Company" />
<EditButton basePath="/users" />
<DeleteButton basePath="/users" />
</Datagrid>
</List>
)
}
In the code block above a couple of changes were made. First, we used the <EmailField>
element to make the links on our email column clickable. Then, we added a label
property to the company column to make their headers more presentable.
Let’s navigate to App.js
and replace ListGuesser
with UserList
:
>/src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import { UserList } from './components/User';
import restProvider from 'ra-data-simple-rest';
const dataProvider = restProvider('http://localhost:3000')
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} />
</Admin>
);
}
export default App;
Using the EditGuesser
Component
Admin pages should be able to edit, delete, and create data as well. react-admin
also does this by using guessers. Add this new line of code in UserList
:
>// /src/components/User.js
import {EditButton, DeleteButton} from 'react-admin'
<EditButton basePath="/users" />
<DeleteButton basePath="/users" />
EditGuesser
edits data of admin pages. In App.js
, import EditGuesser
from react-admin
:
>src/App.js
import React from 'react';
import { Admin, Resource, EditGuesser } from "react-admin";
import { UserList } from './components/User';
import restProvider from 'ra-data-simple-rest';
const dataProvider = restProvider('http://localhost:3000')
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource
name="users"
list={UserList}
edit={EditGuesser}
/>
</Admin>
);
}
}
Now, we can edit user details on our admin interface:
One important thing to note is that simple REST, the data provider we’re using for the fake API, has editing and creating functionalities. What occurs here is that react-admin
displays changes made while simultaneously sending an update query to the data in the db.json
file.
Similar to listing users, a look at our console gives us an idea of what to input as markup. Here’s what we have after using EditGuesser
:
Now, let’s replicate our console’s markup in our application. Append the code sample below in users.js
:
>//src/components/User.js
import React from 'react';
import { Edit, SimpleForm, TextInput } from 'react-admin';
export const UserEdit = (props) => {
return (
<Edit title='Edit User' {...props}>
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="name" />
<TextInput source="username" />
<TextInput source="email" />
<TextInput source="phone" />
<TextInput source="company"/>
</SimpleForm>
</Edit>
)
}
The disabled
attribute in the TextInput
element prevents sensitive properties from being edited. In App.js
, replace EditGuesser
with UserEdit
:
>//src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import { UserList, UserEdit } "./components/authProvider";
import jsonServerProvider from "ra-data-json-server";
const dataProvider = restProvider('http://localhost:3000')
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} edit={UserEdit} />
</Admin>
);
}
}
export default App;
Adding new users
The process of creating a new user is almost the same as editing, except we must create a new set in the db.json
file. In users.js
, replicate the code sample below:
>//src/components/User.js
import React from 'react';
import { Create, SimpleForm, TextInput } from 'react-admin';
export const UserCreate = (props) => {
return (
<Create title='Create User' {...props}>
<SimpleForm>
<TextInput type="number" source="id" />
<TextInput source="name" />
<TextInput source="username" />
<TextInput source="email" />
<TextInput source="phone" />
<TextInput source="company"/>
</SimpleForm>
</Create>
)
}
Now, in App.js
, add the UserCreate
component:
>//src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import { UserList, UserEdit, UserCreate } from './components/User';
const dataProvider = restProvider('http://localhost:3000')
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} create={UserCreate} edit={UserEdit} />
</Admin>
);
}
export default App;
On our interface, let’s try to create a new user:
Similar to what happens when we try to edit the details of a user, optimistic rendering occurs. This explains why in the last seconds of the snippet above, our newly created user displays for a while before the message NOT FOUND
can be seen.
react-admin
authentication
Every admin page needs an authentication process. It can be basic or a bit more complex, such as JSON Web Tokens (JWT) or OAuth.
Although, by default, react-admin
apps do not need authentication to function, it’s still best practice to integrate authentication to admin pages.
react-admin
lets us be flexible with how we implement authentication. Simple REST has no authentication model, so we will create a dummy authentication process that accepts any values as username
and password
, and store these values in localStorage
.
In our src
folder, create a file called authProvider
:
>// src/components/authProvider.js
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
export const authProvider = (type, params) => {
// when a user tries to log in
if (type === AUTH_LOGIN) {
const { username } = params;
localStorage.setItem('username', username)
return Promise.resolve();
}
// when a user tries to logout
if (type === AUTH_LOGOUT) {
localStorage.removeItem('username');
return Promise.resolve();
}
// when the API throws an error
if (type === AUTH_ERROR) {
const { status } = params;
if (status === 401 || status === 403) {
localStorage.removeItem('username');
return Promise.reject()
}
return Promise.resolve()
}
// when a user navigates to a new location
if (type === AUTH_CHECK) {
return localStorage.getItem('username') ?
Promise.resolve() :
Promise.reject();
}
return Promise.reject('Unknown Method');
};
Then head to App.js
and pass the authProvider
property in the <Admin>
component:
>//src/App.js
import React from 'react'
import { Admin, Resource } from 'react-admin'
import restProvider from 'ra-data-simple-rest'
import { authProvider } from "./components/authProvider";
import { UserList, UserEdit, UserCreate} from './components/User';
const dataProvider = restProvider('http://localhost:3000')
function App() {
return (
<Admin dataProvider={dataProvider} authProvider={authProvider}>
<Resource name="users" list={UserList} create={UserCreate} edit={UserEdit} />
</Admin>
);
}
export default App;
Now, when we restart our application, we first get to a login page:
Summary
Creating admin applications does not have to be as complex as it once was. With React’s react-admin
, we can scaffold admin interfaces quite readily. Authentication processes are equally important and they are not excluded here.
You can find the source code for this project here on GitHub.