Futuristic admin interface development scene powered by react-admin

Unleashing the Power of React-Admin: Building Robust Admin Interfaces with Ease

The Orange Cat
The Orange Cat

React-admin is a powerful frontend framework designed to simplify the creation of single-page admin applications. Built on top of React and leveraging Material Design, it provides developers with a comprehensive set of tools and components to streamline the development process. Whether you’re working with REST or GraphQL APIs, react-admin offers the flexibility and features needed to create robust, user-friendly admin panels quickly and efficiently.

Features

React-admin comes packed with an impressive array of features that make it stand out in the world of admin interface development:

  • Backend Agnostic: Connect to any API (REST or GraphQL) with support for over 45 different adapters.
  • Comprehensive Building Blocks: Includes hooks and components for authentication, routing, forms, validation, data grids, search and filtering, relationships, rich text editing, i18n, notifications, menus, theming, and caching.
  • High Quality: Focuses on accessibility, responsiveness, security, performance, and testability.
  • Developer-Friendly: Offers complete documentation, IDE autocompletion, type safety, Storybook integration, demo apps with source code, and a declarative API.
  • Enhanced User Experience: Features like optimistic rendering, filter-as-you-type, undo functionality, and saved queries improve the overall user experience.
  • Customizable: Allows for complete customization by replacing any component with your own implementation.
  • TypeScript Support: Develop in either TypeScript or JavaScript, with opt-in type definitions.

Installation

Getting started with react-admin is straightforward. You can install it along with its required dependencies using npm or yarn:

npm install react-admin
# or
yarn add react-admin

This command installs the core react-admin package along with its peer dependencies. It’s the first step in setting up your admin interface project.

Basic Usage

Let’s dive into a basic example of how to set up a react-admin application:

import * as React from "react";
import { render } from 'react-dom';
import { Admin, Resource } from 'react-admin';
import restProvider from 'ra-data-simple-rest';
import { PostList, PostEdit, PostCreate, PostIcon } from './posts';

render(
  <Admin dataProvider={restProvider('http://localhost:3000')}>
    <Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} icon={PostIcon} />
  </Admin>,
  document.getElementById('root')
);

This code sets up the basic structure of a react-admin application. The Admin component is the root component of a react-admin app. It sets up the routing and ensures that the components you define for each resource are rendered when the route matches. The dataProvider prop is crucial as it tells react-admin how to fetch the data from your API.

The Resource component is used to define the components that will be used for the list, edit, and create views of the ‘posts’ resource. Each of these views (PostList, PostEdit, PostCreate) are custom components that you define to control how the data is displayed and edited.

Now, let’s look at how to define the components for our posts resource:

import * as React from "react";
import {
  List,
  Datagrid,
  Edit,
  Create,
  SimpleForm,
  DateField,
  TextField,
  EditButton,
  TextInput,
  DateInput,
  useRecordContext
} from 'react-admin';
import BookIcon from '@mui/icons-material/Book';

export const PostIcon = BookIcon;

export const PostList = () => (
  <List>
    <Datagrid>
      <TextField source="id" />
      <TextField source="title" />
      <DateField source="published_at" />
      <TextField source="average_note" />
      <TextField source="views" />
      <EditButton />
    </Datagrid>
  </List>
);

const PostTitle = () => {
  const record = useRecordContext();
  return <span>Post {record ? `"${record.title}"` : ''}</span>;
};

export const PostEdit = () => (
  <Edit title={<PostTitle />}>
    <SimpleForm>
      <TextInput disabled source="id" />
      <TextInput source="title" />
      <TextInput source="teaser" options={{ multiline: true }} />
      <TextInput multiline source="body" />
      <DateInput label="Publication date" source="published_at" />
      <TextInput source="average_note" />
      <TextInput disabled label="Nb views" source="views" />
    </SimpleForm>
  </Edit>
);

export const PostCreate = () => (
  <Create title="Create a Post">
    <SimpleForm>
      <TextInput source="title" />
      <TextInput source="teaser" options={{ multiline: true }} />
      <TextInput multiline source="body" />
      <TextInput label="Publication date" source="published_at" />
      <TextInput source="average_note" />
    </SimpleForm>
  </Create>
);

This code defines the list, edit, and create views for the posts resource. The PostList component uses the List and Datagrid components from react-admin to create a table view of all posts. Each field in the table is defined using components like TextField and DateField.

The PostEdit component uses the Edit and SimpleForm components to create a form for editing an existing post. It uses TextInput and DateInput components for each editable field.

The PostCreate component is similar to PostEdit, but it’s used for creating new posts. It doesn’t include fields that wouldn’t be relevant for a new post, like id or views.

The PostTitle component is a custom component used to display the title of the post in the edit view. It uses the useRecordContext hook to access the current record being edited.

Advanced Usage

React-admin offers many advanced features for more complex use cases. Let’s explore some of these features:

Custom Data Provider

You can create a custom data provider to connect react-admin to your specific API:

import { fetchUtils, DataProvider } from 'react-admin';
import { stringify } from 'query-string';

const apiUrl = 'https://my.api.com/';
const httpClient = fetchUtils.fetchJson;

const dataProvider: DataProvider = {
  getList: (resource, params) => {
    const { page, perPage } = params.pagination;
    const { field, order } = params.sort;
    const query = {
      sort: JSON.stringify([field, order]),
      range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
      filter: JSON.stringify(params.filter),
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;

    return httpClient(url).then(({ headers, json }) => ({
      data: json,
      total: parseInt(headers.get('content-range')?.split('/').pop() || '0', 10),
    }));
  },

  getOne: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
      data: json,
    })),

  getMany: (resource, params) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;
    return httpClient(url).then(({ json }) => ({ data: json }));
  },

  getManyReference: (resource, params) => {
    const { page, perPage } = params.pagination;
    const { field, order } = params.sort;
    const query = {
      sort: JSON.stringify([field, order]),
      range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
      filter: JSON.stringify({
        ...params.filter,
        [params.target]: params.id,
      }),
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;

    return httpClient(url).then(({ headers, json }) => ({
      data: json,
      total: parseInt(headers.get('content-range')?.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) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    return httpClient(`${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 },
    })),

  delete: (resource, params) =>
    httpClient(`${apiUrl}/${resource}/${params.id}`, {
      method: 'DELETE',
    }).then(({ json }) => ({ data: json })),

  deleteMany: (resource, params) => {
    const query = {
      filter: JSON.stringify({ id: params.ids }),
    };
    return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
      method: 'DELETE',
    }).then(({ json }) => ({ data: json }));
  }
};

export default dataProvider;

This custom data provider implements all the methods required by react-admin to interact with your API. Each method corresponds to a different type of request:

  • getList: Used to fetch a list of records (for the List view)
  • getOne: Fetches a single record (for the Edit view)
  • getMany: Fetches multiple records by their ids (for reference fields)
  • getManyReference: Fetches multiple records related to another record
  • update: Updates a single record
  • updateMany: Updates multiple records
  • create: Creates a new record
  • delete: Deletes a single record
  • deleteMany: Deletes multiple records

The data provider translates react-admin requests into HTTP requests that match your API’s endpoints and data format. This allows you to use react-admin with any API, regardless of its specific implementation.

Custom Authentication

You can implement custom authentication logic:

import { AuthProvider } from 'react-admin';

const authProvider: AuthProvider = {
  login: ({ username, password }) => {
    const request = new Request('https://example.com/api/auth', {
      method: 'POST',
      body: JSON.stringify({ username, password }),
      headers: new Headers({ 'Content-Type': 'application/json' }),
    });
    return fetch(request)
      .then(response => {
        if (response.status < 200 || response.status >= 300) {
          throw new Error(response.statusText);
        }
        return response.json();
      })
      .then(auth => {
        localStorage.setItem('auth', JSON.stringify(auth));
      });
  },
  logout: () => {
    localStorage.removeItem('auth');
    return Promise.resolve();
  },
  checkError: ({ status }) => {
    if (status === 401 || status === 403) {
      localStorage.removeItem('auth');
      return Promise.reject();
    }
    return Promise.resolve();
  },
  checkAuth: () => {
    return localStorage.getItem('auth')
      ? Promise.resolve()
      : Promise.reject();
  },
  getPermissions: () => {
    const auth = localStorage.getItem('auth');
    const { permissions } = JSON.parse(auth || '{}');
    return Promise.resolve(permissions);
  },
};

export default authProvider;

This custom authentication provider implements the methods required by react-admin to handle authentication:

  • login: Authenticates the user with the provided credentials
  • logout: Removes the user’s authentication data
  • checkError: Checks if an error is due to an authentication issue
  • checkAuth: Verifies if the user is authenticated
  • getPermissions: Retrieves the user’s permissions

In this example, the authentication data is stored in the browser’s local storage. The login method sends a POST request to an authentication endpoint and stores the response. The logout method simply removes this data. The checkError method checks for authentication-related errors, while checkAuth verifies if the authentication data exists in local storage.

Custom Layout

React-admin allows you to customize the layout of your application:

import * as React from 'react';
import { Layout, LayoutProps } from 'react-admin';
import MyAppBar from './MyAppBar';
import MySidebar from './MySidebar';
import MyMenu from './MyMenu';

export const MyLayout = (props: LayoutProps) => (
  <Layout
    {...props}
    appBar={MyAppBar}
    sidebar={MySidebar}
    menu={MyMenu}
  />
);

This code demonstrates how to create a custom layout in react-admin. The MyLayout component extends the default Layout component, allowing you to override specific parts of the layout:

  • appBar: The top bar of the application
  • sidebar: The sidebar containing the menu
  • menu: The menu itself

By providing your own components for these parts (MyAppBar, MySidebar, MyMenu), you can completely customize the look and feel of your admin interface while still leveraging the built-in functionality of react-admin.

Custom Fields and Inputs

You can create custom fields and inputs to suit your specific needs:

import * as React from 'react';
import { useRecordContext } from 'react-admin';
import { Typography } from '@mui/material';

const ColoredNumberField = ({ source }: { source: string }) => {
  const record = useRecordContext();
  const value = record && record[source];
  return (
    <Typography
      variant="body2"
      component="span"
      style={{ color: value > 500 ? 'red' : 'green' }}
    >
      {value}
    </Typography>
  );
};

export default ColoredNumberField;

This code demonstrates how to create a custom field component in react-admin. The ColoredNumberField component is a custom field that displays a number and changes its color based on the value:

  • It uses the useRecordContext hook to access the current record.
  • It retrieves the value from the record using the provided source prop.
  • It renders the value inside a Material-UI Typography component.
  • The color of the text changes to red if the value is greater than 500, otherwise it’s green.

This custom field can be used in place of the standard TextField in your resource components, allowing for more dynamic and visually informative displays of data.

Theming

React-admin uses Material-UI, which makes it easy to customize the look and feel of your application:

import { defaultTheme } from 'react-admin';
import { createTheme } from '@mui/material/styles';

const theme = createTheme({
  ...defaultTheme,
  palette: {
    primary: {
      main: '#3f51b5',
    },
    secondary: {
      main: '#f50057',
    },
  },
});

const App = () => (
  <Admin theme={theme} dataProvider={...}>
    ...
  </Admin>
);

This code shows how to create a custom theme for your react-admin application:

  • It imports the defaultTheme from react-admin and the createTheme function from Material-UI.
  • It creates a new theme by spreading the defaultTheme and overriding specific properties.
  • In this example, it changes the primary and secondary colors of the application.
  • The custom theme is then passed to the Admin component using the theme prop.

By customizing the theme, you can easily change the colors, typography, spacing, and other visual aspects of your admin interface to match your brand or design preferences.

Conclusion

React-admin is a powerful and flexible framework for building admin interfaces. It provides a comprehensive set of tools and components that simplify the process of creating complex, data-driven applications. With its modular architecture, extensive customization options, and support for various data providers, react-admin can adapt to a wide range of project requirements.

Key advantages of using react-admin include:

  1. Rapid development: Pre-built components and hooks allow for quick implementation of common admin features.
  2. Flexibility: The ability to customize and replace any component gives developers full control over the application’s behavior and appearance.
  3. Scalability: React-admin can handle small projects and large, complex applications alike.
  4. Good developer experience: TypeScript support, comprehensive documentation, and a declarative API contribute to a smooth development process.
  5. Enhanced user experience: Features like optimistic rendering and undo functionality create a responsive and user-friendly interface.

Whether you’re building a simple CRUD application or a complex admin dashboard, react-admin provides the tools and flexibility to create robust, efficient, and user-friendly admin interfaces. Its active community and regular updates ensure that it remains a relevant and powerful solution for admin interface development in the React ecosystem.

Comments