How to build an admin panel with React
Tags: react, javascript, tutorial
Published: 2019-08-12 13:06:50

---
title: How to build an admin panel with React
published: true
tags: react, javascript, tutorial 
canonical_url: https://blog.logrocket.com/admin-panel-with-react/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/xomuiephzwxp3y8si7bj.png
---
**Written by [Raphael Ugwu](https://blog.logrocket.com/author/raphaelugwu/)**✏️

## Introduction

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 is needed to sort and handle this data. The usual practice would be to build an interface and connect every endpoint by sending requests to the API. This used to be a tedious process until `react-admin` was introduced.

`react-admin` is a framework that builds admin interfaces by consuming your APIs – Rest, GraphQL or custom. You also won’t have to worry about style formatting as it’s themed with [Material UI](https://material-ui.com/) – a React library used in designing application interfaces. In this blog post, we will learn how `react-admin` scaffolds admin interfaces for applications.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Getting started

Let’s begin by creating a new React project and installing `react-admin` in its directory like this:

```jsx
npx create-react-app react-admin-app
cd react-admin-app
# install react-admin
npm install react-admin
```

Your application should be running with an empty React app on port 3000.

## Modifying and filtering data with guessers

It is essential for an admin page to have CRUD functionalities. We’ll use an API to show how `react-admin` does this. The [JSONPlaceholder](http://jsonplaceholder.typicode.com/) is a dummy REST API which exists for illustration purposes, here’s a sample of the data we’ll be getting:

```jsx
{
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  }
```

We first need to install JSONPlaceholder in our `react-admin` app:

```jsx
npm install ra-data-json-server prop-types
```

Let’s try fetching data from the API. 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 this:

```jsx
import React, { Component } from "react";
import { Admin } from "react-admin";
import jsonServerProvider from "ra-data-json-server";

const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");

class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider} />
    );
  }
}
export default App;
```

Running `npm start` at this point should render an app with a confirmatory message on your browser:

![react admin panel interface](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/reactadmininterface.png?resize=450%2C233&ssl=1)

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 then make decisions on what kind of format the data should be displayed in. Let’s try to display a list of users by applying guessers:

```jsx
import React, { Component } from "react";
import { Admin, Resource, ListGuesser } from "react-admin";
import jsonServerProvider from "ra-data-json-server";

const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");

class App extends Component {
  render() {
    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. The `list` property uses the `<ListGuesser>` component to display this data as a list of users.

![display of data in console](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/datadisplay.png?resize=1225%2C710&ssl=1)

`<ListGuesser>` is not meant to be used in production thus it has to 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:

![data displayed in console](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/console-display-.png?resize=1298%2C614&ssl=1)

So this shows us how our user list should be created. Let’s replicate this data in our application. In the `src` folder of your project, create a file and name it `users.js` :

```jsx
/src/users.js
import React from 'react';
import { List, Datagrid, TextField, EmailField, UrlField } from 'react-admin';
export const UserList = props => (
    <List {...props}>
        <Datagrid rowClick="edit">
            <TextField source="id" />
            <TextField source="name" />
            <TextField source="username" />
            <EmailField source="email" />
            <TextField source="address.street" label="Address" />
            <TextField source="phone" />
            <UrlField source="website" />
            <TextField source="company.name" label="Company" />
        </Datagrid>
    </List>
);
```

In the code block above a couple of changes were made. First, we use the `<UrlField>` element to make the links on our website column clickable. Then we add a `label` property to the address and company columns to make their headers more presentable. Let’s navigate to `App.js` and replace `ListGuesser` with `UserList`:

```jsx
/src/App.js

import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import { UserList } from './users';
import jsonServerProvider from "ra-data-json-server";

const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");

class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider}>
        <Resource name="users" list={UserList} />
      </Admin>
    );
  }
}
export default App;
```

Admin pages should be able to edit and create data as well. `react-admin` also does this by the use of guessers. `EditGuesser` is used to edit data of admin pages. In `App.js` , import `EditGuesser` from `react-admin` :

```jsx
src/App.js

import React from 'react';
import { Admin, Resource, EditGuesser } from "react-admin";
import { UserList } from './users';

class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider}>
        <Resource
          name="users"
          list={UserList}
          edit={EditGuesser}
        />
      </Admin>
    );
  }
}     
```

Now we can edit user details on our admin interface:

![](https://thepracticaldev.s3.amazonaws.com/i/pvb6ez7iyhklxmkfpz0p.gif)

One important thing to note is that JSONPlaceholder, the API we are using doesn’t have editing and creating functionalities. What occurs here is a concept called optimistic rendering – which `react-admin` uses to display changes made while simultaneously sending an update query in the background. Should the query fail, the data will change back to its original form.

Similar to listing users, a look at our console will give us an idea of what to input as markup. Here’s what we have after using `EditGuesser`:

![](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/08/consoledata.png?resize=1363%2C500&ssl=1)

Let’s replicate our console’s markup in our application. Append the code sample below in `users.js`:

```jsx
//src/users.js
import React from 'react';
import { Edit, UrlField, DisabledInput, SimpleForm, TextInput } from 'react-admin';

export const UserEdit = props => (
    <Edit {...props}>
      <SimpleForm>
        <DisabledInput source="id" />
        <TextInput source="name" />
        <TextInput source="username" />
        <TextInput source="email" />
        <TextInput source="address.street" label="Address" />
        <TextInput source="phone" />
        <UrlField source="website" />
        <TextInput source="company.name" label="Company" />
      </SimpleForm>
    </Edit>
  );
```

The `DisabledInput` element prevents sensitive properties from being edited. In `App.js`, replace `EditGuesser` with `UserEdit`:

```jsx
//src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import  { UserList, UserEdit }  from './users';
import jsonServerProvider from "ra-data-json-server";
const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");
class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider}>
        <Resource name="users" list={UserList} edit={UserEdit} />
      </Admin>
    );
  }
}
export default App;
```

The process of creating a new user is almost the same as editing one save for a few elements. In `users.js`, replicate the code sample below:

```jsx
//src/users.js
import React from 'react';
import { Create, UrlField, DisabledInput, SimpleForm, TextInput } from 'react-admin';

export const UserCreate = props => (
    <Create {...props}>
      <SimpleForm>
        <TextInput source="name" />
        <TextInput source="username" />
        <TextInput source="email" />
        <TextInput source="address.street" label="Address" />
        <TextInput source="phone" />
        <UrlField source="website" />
        <TextInput source="company.name" label="Company" />
      </SimpleForm>
    </Create>
  );
```

and in `App.js` add the `UserCreate` component:

```jsx
//src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import  { UserList, UserEdit, UserCreate }  from './users';
import jsonServerProvider from "ra-data-json-server";
const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");
class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider}>
        <Resource name="users" list={UserList} edit={UserEdit} create={UserCreate} />
      </Admin>
    );
  }
}
export default App;
```

On our interface let’s try to create a new user:

![](https://thepracticaldev.s3.amazonaws.com/i/xorifmxx7pz3d989fzl2.gif)
![](https://thepracticaldev.s3.amazonaws.com/i/obiivlya6c5scoaxhiyf.gif)

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 is displayed for a while before the message `NOT FOUND` can be seen.

## Authentication

Every admin page needs an authentication process. It could be basic or a bit more complex (JWT, 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 you be flexible with how you implement authentication. JSONPlaceholder has no authentication model so we are going to create a dummy authentication process which will accept any values as `username` and `password` and store these values in `localStorage`. In your `src` folder, create a file called `authProvider` :

```jsx
// src/authProvider.js
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
   
   export default (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', 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 on to `App.js` and pass the `authProvider` property in the `<Admin>` component:

```jsx
//src/App.js
import React, { Component } from "react";
import { Admin, Resource } from "react-admin";
import authProvider from "./authProvider";
import  { UserList, UserEdit, UserCreate }  from './users';
import jsonServerProvider from "ra-data-json-server";
const dataProvider =
  jsonServerProvider("https://jsonplaceholder.typicode.com");
class App extends Component {
  render() {
    return (
      <Admin dataProvider={dataProvider} authProvider={authProvider}>
        <Resource name="users" list={UserList} edit={UserEdit} create={UserCreate} />
      </Admin>
    );
  }
}
export default App;
```

Now when we restart our application, we first get to a login page:

![](https://thepracticaldev.s3.amazonaws.com/i/z4maairaip6mww6vdex7.gif)

## Summary

Creating admin applications does not have to be as complex as it once was. With `react-admin`, we can scaffold admin interfaces quite readily. Authentication processes are equally important and they are not excluded here. Should you need to take a look at the source code, you can find it [here](https://codesandbox.io/s/react-admin-app-cypx3) on CodeSandBox.
* * *
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/admin-panel-with-react/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [How to build an admin panel with React](https://blog.logrocket.com/admin-panel-with-react/) appeared first on [LogRocket Blog](https://blog.logrocket.com).