React Admin October 2023 Update

Gildas Garcia
Gildas GarciaOctober 02, 2023
#react-admin

What? Another super-long article about react-admin updates? But when am I supposed to read this?

We've heard that feedback: It's sometimes hard to keep up with the pace of updates on react-admin, our open-source frontend framework for building B2B applications on top of REST/GraphQL APIs. The problem is that react-admin becomes better every day, and we only publish a blog post about updates every three months. So, when we do, we have a lot to say (just like the last time).

Let's see the highlights of the past three months of work on react-admin (4.12, 4.13, and 4.14, and Enterprise Edition updates). It's long, but it's worth it.

Ready? Let's dive in.

We were unable to confirm your registration.
Your registration is confirmed.

Keep up to date

Join our react-admin newsletter for regular updates. No spam ever.

Stop wasting time going back to the main list. <PrevNextButtons>, part of React Admin v4.14, lets users navigate between records like invoices or customers swiftly. This component renders navigation buttons linking to the next or previous record within a resource.

<PrevNextButtons> is quite smart. Once users check out the list view for a resource, it won't bug the API again for previous and next links—it uses the react-query cache. Plus, if it needs to hit your API, it keeps things consistent by reusing the list's sorting and filters.

Add it to the actions toolbar of an Edition view, and let your users fly through records.

// in src/CustomerEdit.tsx
import {
    Edit,
    PrevNextButtons,
    ShowButton,
    SimpleForm,
    TextInput,
    TopToolbar,
} from 'react-admin';

export const CustomerEdit = () => (
    <Edit
        actions={
            <TopToolbar>
                <PrevNextButtons />
                <ShowButton />
            </TopToolbar>
        }
    >
        <SimpleForm>
            <TextInput source="first_name" />
            <TextInput source="last_name" />
            <TextInput source="email" />
            <TextInput source="city" />
        </SimpleForm>
    </Edit>
);

Check out the <PrevNextButtons> documentation for more details.

Automatic Application Update With <CheckForApplicationUpdate>

While hosting a static bundle of a Single-Page App is cost-effective and easily deployable, it comes with a challenge. Users who don't refresh their application may never see the updates. This becomes even more problematic if there are simultaneous changes in the API, causing potential mismatches and errors.

React Admin v4.13 introduces the <CheckForApplicationUpdate> component to solve that problem. It prompts users to reload the page when an update is available.

An update notification

To detect updates, <CheckForApplicationUpdate> fetches the current site URL (the index.html) at regular intervals and compares the hash of the response content. It then compares the hash of the returned content. As bundlers typically modify the links to JS and CSS bundles following a code change, this method effectively recognizes an update.

To add this feature to your application, include <CheckForApplicationUpdate> in a custom layout and you're good to go:

// in src/MyLayout.tsx
import { CheckForApplicationUpdate, Layout, LayoutProps } from 'react-admin';

export const MyLayout = ({ children, ...props }: LayoutProps) => (
    <Layout {...props}>
        {children}
        <CheckForApplicationUpdate />
    </Layout>
);

// in src/App.tsx
import { Admin, ListGuesser, Resource } from 'react-admin';
import { MyLayout } from './MyLayout';

export const App = () => (
    <Admin layout={MyLayout}>
        <Resource name="posts" list={ListGuesser} />
    </Admin>
);

Check out the <CheckForApplicationUpdate> documentation for more details.

Validating The Uniqueness Of a Field With useUnique

Validating that a value is unique (e.g. a username) is a very common requirement. Before React Admin v4.12, you had two options to implement this control in a Create or Edit view, both challenging:

React Admin now provides the useUnique hook to make this easier. It accounts for eventual filters (e.g. unique username within an organization), excludes the current record in edit views, and takes care of not calling your API too many times to avoid lags in the UI or rate limitings errors, etc.

This validator is a good candidate for the validate prop of any Input component:

import { SimpleForm, TextInput, useUnique } from 'react-admin';

const UserCreateForm = () => {
    const unique = useUnique();
    return (
        <SimpleForm>
            <TextInput source="username" validate={unique()} />
        </SimpleForm>
    );
};

Check the useUnique documentation for more details.

AI Powered Authoring With <PredictiveTextInput> And <SmartRichTextInput>

Thanks to new AI changes, many tasks that needed hands-on work can now be done automatically. We looked into how to use these changes to improve the user experience and productivity of admin and B2B apps. The result is a set of new Form inputs that use Large Language Models to help users write better and more quickly.

The first one is <PredictiveTextInput>. It's a drop-in replacement for the default <TextInput> component that brings autocompletion to your forms, based on the current form values.

The second input, <SmartRichTextInput>, extends the <RichTextInput> component with edition brushes to auto-correct, rephrase, summarize or extend the current selection.

Use them like any other react-admin inputs:

import { Edit, SimpleForm, TextInput } from 'react-admin';
import { PredictiveTextInput, SmartRichTextInput } from '@react-admin/ra-ai';

const PersonEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="firstName" />
            <TextInput source="lastName" />
            <TextInput source="company" />
            <PredictiveTextInput source="email" />
            <PredictiveTextInput source="website" />
            <PredictiveTextInput source="bio" multiline />
        </SimpleForm>
    </Edit>
);

export const PostEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="title" />
            <SmartRichTextInput source="body" />
        </SimpleForm>
    </Edit>
);

These two components are part of ra-ai, which is an Enterprise Edition module. For more details, you can read our article highlighting the ra-ai module, or read the documentation for the inputs:

<RecordRepresentation>, A New Component That Renders a Record Reference

There are several places where react-admin needs to render a string representation for a record:

You can already define the default record representation for each resource using the <Resource recordRepresentation> prop.

<Resource
    name="users"
    list={UserList}
    recordRepresentation={record => `${record.first_name} ${record.last_name}`}
/>

However, if you needed this representation in a custom component, you had to rely on the useGetRecordRepresentation and the useRecordContext hooks. React-admin v4.13.0 makes it easier by introducing the <RecordRepresentation> component:

// in src/posts/PostBreadcrumbs.tsx
import * as React from "react";
import { Breadcrumbs, Typography } from "@mui/material";
-import { Link, useGetRecordRepresentation, useRecordContext, useResourceContext } from "react-admin";
+import { Link, RecordRepresentation } from "react-admin";

export const PostBreadcrumbs = () => {
-    const record = useRecordContext();
-    const resource = useResourceContext();
-    const getRecordRepresentation = useGetRecordRepresentation(resource);
    return (
        <div role="presentation">
            <Breadcrumbs aria-label="breadcrumb">
                <Link underline="hover" color="inherit" to="/">
                    Home
                </Link>
                <Link underline="hover" color="inherit" to="/posts">
                    Posts
                </Link>
-                <Typography color="text.primary">{getRecordRepresentation(record)}</Typography>
+                <Typography color="text.primary"><RecordRepresentation /></Typography>
            </Breadcrumbs>
        </div>
    );
};

// in src/posts/PostEdit.tsx
import { EditBase, EditView, SimpleForm, TextInput } from "react-admin";
import { PostBreadcrumbs } from "./PostBreadcrumbs";

const PostEdit = () => (
    <EditBase>
        <PostBreadcrumbs />
        <EditView>
            <SimpleForm>
                <TextInput source="title" />
            </SimpleForm>
        </EditView>
    </EditBase>
);

Speaking of recordRepresentation, the <SimpleList> component now uses it as the default primaryText, making it even faster to use:

const UserList = () => (
  <List>
-    <SimpleList primaryText={(record) => `${record.first_name} ${record.last_name}`} />
+    <SimpleList />
  </List>
)

Find more details in the <RecordRepresentation> documentation.

<UpdateButton>, A Faster Way To Add Simple Update Actions

Many buttons in admin apps trigger an update on the current record. For instance, a "Mark as read" button on a notification, a "Archive" button on a message, etc. You can implement such buttons using the data provider hooks, but it's a bit verbose and requires understanding the react-query useMutation hook.

React-admin v4.13 introduces the <UpdateButton> component to simplify this process. It renders a button that triggers an update on the current record when clicked. Its API is much simpler than the useMutation hook:

import {
    List,
    Datagrid,
    DateField,
    TextField,
-   Button,
-   useDataProvider,
-   useRecordContext,
+   UpdateButton,
} from 'react-admin';
-import { useMutation } from 'react-query';

-const MarkAsReadButton = () => {
-    const record = useRecordContext();
-    const dataProvider = useDataProvider();
-    const { mutate, isLoading } = useMutation(
-        () => dataProvider.update(
-           'notifications',
-           { id: record.id, data: { read: true } }
-        ),
-    );
-    return (
-       <Button
-           label="Mark as read"
-           onClick={() => mutate()}
-           disabled={isLoading}
-       />
-    );
-};

export const NotificationList = () => (
    <List filter={{ read: false }}>
        <Datagrid>
            <DateField source="date" />
            <TextField source="body" />
-           <MarkAsReadButton />
+           <UpdateButton label="Mark as read" data={{ read: true }} />
        </Datagrid>
    </List>
);

UpdateButton

You can get more details in the <UpdateButton> documentation.

Easy Bulk Edition With <BulkUpdateFormButton> And <InputSelectorForm>

Users often have to repeat the same changes on multiple records. For instance, they may want to mark several notifications as read, or update the publication date of several posts. Instead of editing each record one by one, they should be able to select multiple records and edit them all at once.

In ra-form-layout v4.9.0, we introduced the <BulkUpdateFormButton> component that streamlines this process.

This component renders a button that opens a dialog containing any form you want. On submit, the form calls the dataProvider.updateMany() method to update the selected records using the values from the form.

<BulkUpdateFormButton> uses a similar syntax to what you're used to writing for a standard Edit view:

import {
    BooleanField,
    BooleanInput,
    Datagrid,
    DateField,
    DateInput,
    List,
    SimpleForm,
    TextField,
} from 'react-admin';
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';

const PostBulkUpdateButton = () => (
    <BulkUpdateFormButton>
        <SimpleForm>
            <DateInput source="published_at" />
            <BooleanInput source="is_public" />
        </SimpleForm>
    </BulkUpdateFormButton>
);

const PostList = () => (
    <List>
        <Datagrid bulkActionButtons={<PostBulkUpdateButton />}>
            <TextField source="id" />
            <TextField source="title" />
            <DateField source="published_at" />
            <BooleanField source="is_public" />
        </Datagrid>
    </List>
);

But there's more. The <InputSelectorForm> allows your users to choose which fields they want to change.

This UI is especially useful when you have a lot of fields to edit, or non-null default values. <InputSelectorForm> takes an array of inputs as a prop. Each input is an object with a label and an element property.

import {
    BooleanInput,
    DateInput,
    SelectArrayInput,
    TextInput,
} from 'react-admin';
import { InputSelectorForm } from '@react-admin/ra-form-layout';

const PostEdit = () => (
    <InputSelectorForm
        inputs={[
            {
                label: 'Title',
                element: <TextInput source="title" />,
            },
            {
                label: 'Body',
                element: <TextInput source="body" multiline />,
            },
            {
                label: 'Published at',
                element: <DateInput source="published_at" />,
            },
            {
                label: 'Is public',
                element: <BooleanInput source="is_public" />,
            },
            {
                label: 'Tags',
                element: (
                    <SelectArrayInput
                        source="tags"
                        choices={[
                            { id: 'react', name: 'React' },
                            { id: 'vue', name: 'Vue' },
                            { id: 'solid', name: 'Solid' },
                            { id: 'programming', name: 'Programming' },
                        ]}
                    />
                ),
            },
        ]}
    />
);

Combine <InputSelectorForm> with the <BulkUpdateFormButton> to give your users superpowers.

import { BooleanInput, DateInput } from 'react-admin';
import {
    BulkUpdateFormButton,
    InputSelectorForm,
} from '@react-admin/ra-form-layout';

const PostBulkUpdateButton = () => (
    <BulkUpdateFormButton>
        <InputSelectorForm
            inputs={[
                {
                    label: 'Published at',
                    element: <DateInput source="published_at" />,
                },
                {
                    label: 'Is public',
                    element: <BooleanInput source="is_public" />,
                },
            ]}
        />
    </BulkUpdateFormButton>
);

The <BulkUpdateFormButton> and <InputSelectorForm> are Enterprise Edition components, part of the ra-form-layout package. You can learn more about them in the documentation:

<SolarLayout>: A New Layout Optimized For Large Applications

The default <Layout> may be suitable for many situations, but its large sidebar and top bar limit screen space for large and complex applications. Our second layout, <ContainerLayout>, only works for apps with a small number of menu items. For a minimalist navigation experience, we introduce the <SolarLayout> which eliminates the top bar and features a slim menu, maximizing screen usability.

react-admin layouts

The menu items can reveal a secondary panel to show sub-menus, preference forms, a search engine, etc. It's ideal for applications with a large number of resources.

On mobile, <SolarLayout> falls back to an AppBar to save horizontal space:

<SolarLayout> is a drop-in replacement for the default <Layout>. You can use it by setting the layout prop of the <Admin> component:

import { Admin, Resource, ListGuesser } from 'react-admin';
import { SolarLayout } from '@react-admin/ra-navigation';

export const App = () => (
    <Admin dataProvider={dataProvider} layout={SolarLayout}>
        <Resource name="songs" list={ListGuesser} />
        <Resource name="artists" list={ListGuesser} />
    </Admin>
);

If you need a custom menu, you can leverage <SolarMenu>. It is very similar to what you're used to with the default <Menu> component:

import { Admin, Resource, ListGuesser } from 'react-admin';
import {
    SolarLayoutProps,
    SolarLayout,
    SolarMenu,
} from '@react-admin/ra-navigation';
import { dataProvider } from './dataProvider';

const CustomMenu = () => (
    <SolarMenu>
        <SolarMenu.Item
            label="Sales"
            subMenu={
                <SolarMenu.List>
                    <SolarMenu.ResourceItem name="orders" />
                    <SolarMenu.ResourceItem name="invoices" />
                </SolarMenu.List>
            }
        />
        <SolarMenu.Item
            label="Catalog"
            subMenu={
                <SolarMenu.List>
                    <SolarMenu.ResourceItem name="products" />
                    <SolarMenu.ResourceItem name="categories" />
                </SolarMenu.List>
            }
        />
        <SolarMenu.Item
            label="Customers"
            subMenu={
                <SolarMenu.List>
                    <SolarMenu.Item
                        name="customers.all"
                        label="All customers"
                        to={`/customers?filter=${encodeURIComponent(
                            JSON.stringify({ filter: {} }),
                        )}`}
                    />
                    <SolarMenu.Item
                        name="customers.new"
                        label="New customers"
                        to={`/customers?filter=${encodeURIComponent(
                            JSON.stringify({
                                filter: {
                                    last_seen_gte: endOfYesterday().toISOString(),
                                },
                            }),
                        )}`}
                    />
                </SolarMenu.List>
            }
        />
        <SolarMenu.Item
            label="Reviews"
            subMenu={
                <SolarMenu.List>
                    <SolarMenu.Item
                        name="reviews.all"
                        label="New reviews"
                        to={`/reviews?filter=${encodeURIComponent(
                            JSON.stringify({ filter: {} }),
                        )}`}
                    />
                    <SolarMenu.Item
                        name="reviews.pending"
                        label="Pending reviews"
                        to={`/reviews?filter=${encodeURIComponent(
                            JSON.stringify({ filter: { status: 'pending' } }),
                        )}`}
                    />
                    <SolarMenu.Item
                        name="reviews.bad"
                        label="Bad reviews"
                        to={`/reviews?filter=${encodeURIComponent(
                            JSON.stringify({ filter: { rating_lte: 2 } }),
                        )}`}
                    />
                </SolarMenu.List>
            }
        />
        <SolarMenu.ResourceItem name="stores" />
        <SolarMenu.ResourceItem name="events" />
    </SolarMenu>
);

<SolarLayout> is an Enterprise Edition component, part of the ra-navigation package. You can learn more about it in the <SolarLayout> documentation.

Improved List Design and UX

On list pages with no filter form, the content used to come after a large top margin:

A list page with no filters, showing a space above the list

We removed this margin to make more room for your data:

A list page with no filters, showing no space above the list

Besides, we've changed the UI for the filter form. The "clear" button that allows to remove an input from the form filter is now placed after the input instead of before is:

Moreover, on small devices, users would see a vertical list of filters with the remove buttons first and actions were below the filters, making them hard to spot. We now display the actions first, the filters below them, and the remove buttons at the end of each filter line:

Before After
Old filters on small devices New filters on small devices

Finally, we improved the design of inputs on small devices by making them full width:

Before After
A form displayed on small devices with inputs of various widths A form displayed on small devices with full-width inputs

Overall, both the desktop and mobile UI now feel more intuitive, and the list page is more compact, leaving more room for your data.

<TranslatableInputs> now allow vertical layout

The <TranslatableInputs> displays one input per row by default. This works well on small devices, but it's a waste of space on desktop:

TranslatableInputs with an vertical layout

React Admin v4.13.0 lets you opt in to a row layout for <TranslatableInput>. We exposed the props of the underlying <Stack> component's props to let you override the direction, gap, and overall style::

<TranslatableInputs locales={['en', 'fr']} StackProps={{ direction: 'row' }}>
    <TextInput source="title" />
    <TextInput source="description" sx={{ marginLeft: 2 }} />
</TranslatableInputs>

TranslatableInputs with an horizontal layout

Check the <TranslatableInputs> documentation for more details.

Microsoft Azure Active Directory Auth Provider

We added plug-and-play support for the Microsoft Authentication Library (MSAL). This is useful when using Azure Active Directory to authenticate your users.

Check the ra-auth-msal package documentation for more details.

Google Workplace Auth Provider

We also added plug-and-play support for Google Workplace to authenticate your users using the Google Identity Services (GIS).

React-admin with an opened menu showing Google Workplace login options

Check the ra-auth-google package documentation for more details.

Support For Lazy-loaded React Components

The Single Page Application architecture allow all sorts of performance optimizations - and react-admin uses them all to make your apps super snappy. However, as an application grows in size, so does its JS bundle. Thanks to React Suspense and the lazy function, it's possible to load parts of your application on-demand, reducing the size of the main JS bundle.

However, before React Admin v4.14, using React.lazy() required writing a custom layout. Not anymore! React-admin's <Layout> now uses <Suspense>, so you can lazy load parts of your application and let react-admin gracefully handle the loading state.

This is great for pages that users don't visit often, whether they are resources pages or custom routes:

// in src/App.tsx
import * as React from 'react';
import { Admin, Resource, CustomRoutes } from 'react-admin';
import { Route } from 'react-router-dom';

import { dataProvider } from './dataProvider';
import { users } from './users';

const PostList = React.lazy(() => import('./posts/PostList'));
const PostEdit = React.lazy(() => import('./posts/PostEdit'));
const Analytics = React.lazy(() => import('./analytics/Analytics'));

const App = () => (
    <Admin dataProvider={dataProvider}>
        <Resource name="users" {...users} />
        <Resource name="posts" list={PostList} edit={PostEdit} />
        <CustomRoutes>
            <Route path="/analytics" component={Analytics} />
        </CustomRoutes>
    </Admin>
);

You can lazy-load components at a more granular level. For instance, the <RichTextInput> relies on Quill.js, and is quite heavy. Now you just have to lazy-load it as follows:

import { Edit, SimpleForm, TextInput } from 'react-admin';
-import { RichTextInput } from 'ra-input-rich-text';

+const RichTextInput = React.lazy(() =>
+    import('ra-input-rich-text').then(module => ({
+        default: module.RichTextInput,
+    }))
+);

export const PostEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="title" />
            <RichTextInput source="body" />
        </SimpleForm>
    </Edit>
);

You can see lazy loading in action in our CRM demo: The Deals page, which features a Trello-like interface with drag & drop, is lazy-loaded when accessed for the first time.

Check the documentation about lazy-loading <Resource> components for more details.

Easier Theme-Aware Row Styling With <Datagrid rowSx>

It has always been possible to customize a <Datagrid> row based on its record values thanks to the rowStyle prop. However, it wasn't so easy to use your theme values in the custom style as you had to use the useTheme hook.

React-admin v4.13.0 introduces the <Datagrid rowSx> prop that makes it easier to use your theme values:

import { List, Datagrid } from "react-admin";
-import { useMemo } from "react";
-import { useTheme, Theme } from "@mui/material";

+const postRowSx = (record, index) => ({
+    backgroundColor: record.nb_views >= 500 ? `info.main` : 'background.paper',
+});

export const PostList = () => {
-    const theme = useTheme<Theme>();
-    const postRowStyle = useMemo(record, index) => ({
-        backgroundColor: record.nb_views >= 500 ? theme.palette.info.main : theme.palette.background.paper,
-    }, [theme]);

    return (
        <List>
-            <Datagrid rowStyle={postRowStyle}>...</Datagrid>
+            <Datagrid postRowSx={postRowSx}>...</Datagrid>
        </List>
    );
}

Check the <Datagrid rowSx> documentation for more details.

<DateField> And <NumberField> Now Accept a transform Prop

<DateField> and <NumberField> are great components to format a native date or number. But many APIs return string values for dates and numbers, forcing you to use the <FunctionField> instead, and do the formatting by hand.

React Admin v4.13 introduces a transform prop in <DateField> and <NumberField>, to let you transform the value before formatting it:

<DateField
    source="published_at"
    transform={value =>
        new Date(value.replace(/(\d{2})-(\d{2})-(\d{2})/, '20$3-$1-$2'))
    }
 />
// renders the record { id: 1234, published_at: '01-23-22' } as
<span>01/23/2022</span>

<NumberField source="price" transform={v => Math.floor(v / 100)} />
// renders the record { id: 1234, price: '123456' } as
<span>1234.56</span>

Check the <DateField> documentation and the <NumberField> documentation for more details.

Allow Disabling Store Persistence Of The List Parameters

It's a common use case to display a <List> on a dashboard or any other custom page. However, the list parameters are persisted in the store by default, which means that if you navigate to a resource page and come back to your dashboard, the list will be displayed with the same filters, sort, and pagination parameters. This is not always the desired behavior.

In react-admin v4.12.0, it is now possible to disable the store persistence of the list parameters by setting the storeKey prop to false on the <List> component:

const MyDashboard = () => (
    <Stack direction="row">
        <List resource="orders" storeKey={false}>
            ...
        </List>
        <List resource="reviews" storeKey={false}>
            ...
        </List>
        <List resource="customers" storeKey={false}>
            ...
        </List>
    </Stack>
);

Multiple lists

Check the <List storeKey> documentation for more details.

Reload Permissions On Demand

Some applications are designed so that users have different permissions depending on the context. For instance, a user having access to two organizations might not have the same permissions for both. In this case, the permissions must be reloaded when the context changes.

This was very difficult to achieve before react-admin v4.12.0. The usePermissions hook now returns a refresh function that allows you to reload the permissions on demand:

const ChooseOrganization = () => {
    const [organizations, setActiveOrganization] = useUserOrganizations();
    const { refetch } = usePermissions();

    const handleChange = event => {
        setActiveOrganization(event.target.value);
        refetch();
    };

    return (
        <Select
            labelId="demo-simple-select-label"
            id="demo-simple-select"
            value={age}
            label="Age"
            onChange={handleChange}
        >
            {organizations.map(organization => (
                <MenuItem key={organization.id} value={organization.id}>
                    {organization.name}
                </MenuItem>
            ))}
        </Select>
    );
};

Check the usePermissions documentation for more details.

Key Concepts And Best Practices

React Admin has grown quite a bit since its humble beginnings. Since it's now a large framework, it has became more difficult to understand how it works. Besides, many newcomers have legitimate questions about how to structure their applications.

To make it easier, we added a Key Concepts page that explains the architectural decisions we made, the patterns we use, and the different principles that drive our work.

Architecture page

We strongly advise you to get a look at this page, together with the Features page, which highlights the main features of react-admin.

Improved Theming documentation

Designing a react-admin app according to a brand style guide, or an existing design system, is a very common demand. However, it was hard to find the correct guidelines for doing so in the previous documentation.

We restructured the Theming page into a dedicated section with multiple pages to explain how to customize the design of react-admin apps:

Theming page

In addition, we introduced dedicated pages for several react-admin key components for design, such as:

This improved documentation will make it easier for you to adjust your app design in a pixel-perfect way.

TypeScript Examples

React-admin is developed in TypeScript and exposes types for its components and hooks. However, we know that it's not always easy to use these types in a react-admin application. One of the main pain points was that the documentation examples were written in JavaScript.

In addition to a new TypeScript sections, we started to rewrite the documentation examples in TypeScript. We're not done yet but we are progressing.

The Tutorial, for instance, now works in both JavaScript and TypeScript. And many code examples offer a TypeScript/JavaScript toggle:

We hope this will help you to use TypeScript in your react-admin projects.

Document The useRegisterMutationMiddleware Hook

You might remember we introduced a middleware mechanism back in react-admin v4.1. Middleware functions “wrap” the main mutation (dataProvider.create() in a Creation page, dataProvider.update() in an Edition page), so you can add your own code to be executed before and after it. This allows you to perform various advanced form use cases, such as:

  • transforming the data passed to the main mutation,
  • updating the mutation parameters before it is called,
  • creating, updating or deleting related data,
  • adding performances logs,
  • etc.

As powerful as it is, te mutation middleware feature wasn't properly documented and only users who read the source code could use it.

We added a dedicated documentation page to explain how it works and how to use it, with examples.

Mutation Middlewares documentation

To be honest, we pondered for some time before exposing this API, because it is VERY powerful. So remember: with great power comes great responsibility.

We believe that <Breadcrumb> component, which renders a breadcrumb path that automatically adapts to the page location, is super smart and saves developers tons of time.

However, the poor documentation for this component wasn't doing it justice, and many readers didn't see the point of using it.

We rewrote the <Breadcrumb> documentation from the ground up to make it easier to understand, adding a detailed review of the underlying mechanisms such as the AppLocationContext and providing a lot more examples with screenshots and code snippets.

Breadcrumb documentation

Miscellaneous Changes

Believe it or not, there were many more changes in the last 3 months than the ones listed above. We can't cover them all, so here is a quick list of the other changes:

Conclusion

React-admin is constantly improving, helping developers around the world to build awesome web applications, faster.

The huge changelog above is the result of the hard work of the marmelab team and of the react-admin community. We'd like to thank all the contributors who helped us make react-admin better.

If you want to avoid reading super long articles about react-admin updates in the future, watch the marmelab/react-admin repository to be notified of new releases (one bug fix release every week, one minor release with new features every month).

And please come to our Discord server to get daily updates, tips & tricks, and a friendly community to help you with your react-admin projects!

Did you like this article? Share it!