Raphael Ugwu Writer, software engineer, and a lifelong student.

How to create a React admin panel

7 min read 2142

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:

We made a custom demo for .
No really. Click here to check it out.

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": "[email protected]",
      "phone": 37802223,
      "company": "Hunters Mobile Co."
    },
    {
      "id": "2",
      "name": "Peggy Sonaya",
      "username": "Peggaya",
      "email": "[email protected]",
      "phone": 4402673,
      "company": "Peggy's Drycleaning"
    },
    {
      "id": "3",
      "name": "Joseph Maguire",
      "username": "Joemag",
      "email": "[email protected]",
      "phone": 224423045,
      "company": "Jojo's Pot"
    },
    {
      "id": "4",
      "name": "Jon Hanus",
      "username": "Hanuson",
      "email": "[email protected]",
      "phone": 89354033,
      "company": "Joe's Funeral Home"
    },
    {
      "id": "5",
      "name": "Ron Burgundy",
      "username": "Colorburgundy",
      "email": "[email protected]",
      "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:

React-admin default page

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.

User Data Displayed In List

<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:

ListGuesser Showing Users In A List

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:

List after using EditGuesser, Shows List In Vertical Column With Code On The Right Side

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.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Raphael Ugwu Writer, software engineer, and a lifelong student.

Testing accessibility with Storybook

One big challenge when building a component library is prioritizing accessibility. Accessibility is usually seen as one of those “nice-to-have” features, and unfortunately, we’re...
Laura Carballo
4 min read

5 Replies to “How to create a React admin panel”

  1. Outstanding, and thank you for this informative blog on ‘react-admin’! Just a not about the code, which is corrected codesandbox….., the username variable should be left out in the authentication component in the following manner. Referring to the codesandbox shows the correction made:

    // when a user tries to logout
    if (type === AUTH_LOGOUT) {
    localStorage.removeItem(‘username’)
    return Promise.resolve();
    }

    I’ve learned a lot in a relatively brief period of instruction and it’s much appreciated.

    RL Glover

  2. You may want to update some parts in here…
    The DisabledInput Component Was Removed
    You can replace with a disabled or read-only TextInput.

Leave a Reply