ra-editable-datagrid
The default react-admin user-experience consists of 3 pages: List, Edit, and Create. However, in some cases, users may prefer to do all search and edition tasks in one page, allowing for an "edit-in-place" experience.
To this purpose, this package offers the <EditableDatagrid> component, a drop in replacement for <Datagrid> that allows users to edit, create, and delete records in place.
With <EditableDatagrid>, when users click on a row in the datagrid, the row content is replaced by the edition form. They can also create new records by clicking on the Create button, which inserts an empty editable row as the first line of the list. Finally, they can delete a record by clicking on the Delete button on each row.
You can test it live in the Enterprise Edition Storybook and the e-commerce demo.
<EditableDatagrid> allows you to use any Input component to edit the record - including Reference Inputs for foreign keys.
Installation
npm install --save @react-admin/ra-editable-datagrid
# or
yarn add @react-admin/ra-editable-datagrid
Tip: ra-editable-datagrid is part of the React-Admin Enterprise Edition, and hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.
<EditableDatagrid>
Usage
<EditableDatagrid> renders like a regular <Datagrid>, adding some controls to create, edit, and delete records in place.
Replace <Datagrid> with <EditableDatagrid> in a react-admin <List>, <ReferenceManyField>, or any other component that creates a ListContext. In addition, pass a form component to be displayed when the user switches to edit or create mode.
import {
List,
ListActions,
TextField,
TextInput,
DateField,
DateInput,
SelectField,
SelectInput,
required,
} from 'react-admin';
import { EditableDatagrid, RowForm } from '@react-admin/ra-editable-datagrid';
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid createForm={<ArtistForm />} editForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
<DateField source="dob" label="born" />
<SelectField source="profession" choices={professions} />
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" validate={required()} />
<TextInput source="lastName" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="profession" choices={professions} />
</RowForm>
);
const professions = [
{ id: 'actor', name: 'Actor' },
{ id: 'singer', name: 'Singer' },
{ id: 'other', name: 'Other' },
];
import {
List,
ListActions,
TextField,
TextInput,
DateField,
DateInput,
SelectField,
SelectInput,
required,
} from "react-admin";
import { EditableDatagrid, RowForm } from "@react-admin/ra-editable-datagrid";
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid createForm={<ArtistForm />} editForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
<DateField source="dob" label="born" />
<SelectField source="profession" choices={professions} />
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" validate={required()} />
<TextInput source="lastName" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="profession" choices={professions} />
</RowForm>
);
const professions = [
{ id: "actor", name: "Actor" },
{ id: "singer", name: "Singer" },
{ id: "other", name: "Other" },
];
Tip: No need to include an <EditButton> in the datagrid children, as <EditableDatagrid> automatically adds a column with edit/delete buttons.
Props
As <EditableDatagrid> is a drop-in replacement for <Datagrid>, it accepts all the <Datagrid> props, plus a few extra props:
| Prop | Required | Type | Default | Description |
|---|---|---|---|---|
actions |
Optional | ReactElement | - | The component used to customize the actions buttons. |
createForm |
Optional | ReactElement | - | The component to display as the first row when the user creates a record. |
editForm |
Required | ReactElement | - | The component to display instead of a row when the users edit a record. |
disableAutofocus |
Optional | boolean | false |
Disable the autofocus for create and edit forms |
mutationMode |
Optional | string |
undoable |
Mutation mode ('undoable', 'pessimistic' or 'optimistic'). |
noDelete |
Optional | boolean | - | Disable the inline Delete button. |
actions
By default, the <EditableDatagrid> will show both edit and delete buttons when users hover a row. If you want to either customize the button's behavior or provide more actions, you can leverage the actions prop. It accepts a React element.
For instance, here's how to customize the delete button so that it asks users for a confirmation but still allows them to undo the deletion:
import React from 'react';
import { List, TextField } from 'react-admin';
import {
DeleteWithConfirmIconButton,
EditableDatagrid,
EditRowButton,
} from '@react-admin/ra-editable-datagrid';
import { ArtistForm } from './ArtistForm';
export const ArtistList = () => (
<List>
<EditableDatagrid
actions={<RowAction />}
// The mutation mode is still applied to updates
mutationMode="undoable"
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="name" />
</EditableDatagrid>
</List>
);
const RowAction = () => (
<>
<EditRowButton />
<DeleteWithConfirmIconButton mutationMode="undoable" />
</>
);
import React from "react";
import { List, TextField } from "react-admin";
import { DeleteWithConfirmIconButton, EditableDatagrid, EditRowButton } from "@react-admin/ra-editable-datagrid";
import { ArtistForm } from "./ArtistForm";
export const ArtistList = () => (
<List>
<EditableDatagrid
actions={<RowAction />}
// The mutation mode is still applied to updates
mutationMode="undoable"
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="name" />
</EditableDatagrid>
</List>
);
const RowAction = () => (
<>
<EditRowButton />
<DeleteWithConfirmIconButton mutationMode="undoable" />
</>
);
createForm
The component displayed as the first row when a user clicks on the Create button. It's usually a form built with <RowForm>, with the same number of children as the <EditableDatagrid> has children.
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid
editForm={<ArtistForm />}
createForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
{/*...*/}
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
{/*...*/}
</RowForm>
);
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid editForm={<ArtistForm />} createForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
{/*...*/}
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
{/*...*/}
</RowForm>
);
Tip: It's a good idea to reuse the same form component for createForm and editForm, as in the example above.
Since the creation form is embedded in the List view, you shouldn't set the <Resource create> prop. But react-admin's <List> only displays a Create button if the current Resource has a create page. That's why you must force the <List actions> value, as in the example above, to have the Create button show up with <EditableDatagrid>.
Also, when the list is empty, the <List> component normally doesn't render its children (it renders an empty component instead). To bypass this system and see the empty editable datagrid with a create button instead, you need to force the <List empty={false}> prop, as in the example above.
<EditableDatagrid> renders the createForm elements in a <table>, so the create form element should render a <tr>. We advise you to use the <RowForm> component, which renders a <tr> by default. But you can also use your own component to render the creation form (see <RowForm> below).
Tip: The createForm component must render as many columns as there are children in the <EditableDatagrid>. That's why in the example above, the <ArtistForm> component renders a <TextInput> for each <TextField> (except for the read-only id field, for which it renders a <TextField>).
Tip: To display a create button on top of the list, you should add a <ListActions hasCreate /> component to the actions prop of the <List> component, as in the example below.
Tip: To display a custom create button, pass a custom component as the empty prop. It can use the useEditableDatagridContext hook to access to openStandaloneCreateForm and closeStandaloneCreateForm callbacks.
editForm
The component displayed when a user clicks on a row to edit it. It's usually a form built with <RowForm>, with the same number of children as the <EditableDatagrid> has children.
export const ArtistList = () => (
<List>
<EditableDatagrid editForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
{/*...*/}
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
{/*...*/}
</RowForm>
);
export const ArtistList = () => (
<List>
<EditableDatagrid editForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
{/*...*/}
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
{/*...*/}
</RowForm>
);
Tip: No need to include a <SaveButton> in the form, as <RowForm> automatically adds a column with save/cancel buttons.
Tip: If one column isn't editable, use a <Field> component instead of an <Input> component (like the <TextField> in the <RowForm> above).
The <EditableDatagrid> component renders the editForm elements in a <table>, so these elements should render a <tr>. We advise you to use the <RowForm> component for editForm, which renders a <tr> by default. But you can also use your own component to render the row form.
disableAutofocus
disableAutofocus disables the auto focus of the first row input when creating or editing a row.
<EditableDatagrid disableAutofocus>
{/*...*/}
</EditableDatagrid>
mutationMode
Use the mutationMode prop to specify the mutation mode for the edit and delete actions. By default, the <EditableDatagrid> uses the undoable mutation mode. You can change it to optimistic or pessimistic if you prefer.
<EditableDatagrid mutationMode="pessimistic">
{/*...*/}
</EditableDatagrid>
noDelete
You can disable the delete button by setting the noDelete prop to true:
<EditableDatagrid noDelete>
{/*...*/}
</EditableDatagrid>

<RowForm>
<RowForm> renders a form in a table row, with one table cell per child. It is designed to be used as editForm and createForm element.
Usage
<RowForm> and <EditableDatagrid> should have the same number of children, and these children should concern the same source.
import { List, ListActions, TextField, TextInput } from 'react-admin';
import { EditableDatagrid, RowForm } from '@react-admin/ra-editable-datagrid';
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid
createForm={<ArtistForm />}
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
</RowForm>
);
import { List, ListActions, TextField, TextInput } from "react-admin";
import { EditableDatagrid, RowForm } from "@react-admin/ra-editable-datagrid";
export const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid createForm={<ArtistForm />} editForm={<ArtistForm />}>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
</EditableDatagrid>
</List>
);
const ArtistForm = () => (
<RowForm>
<TextField source="id" />
<TextInput source="firstName" />
<TextInput source="lastName" />
</RowForm>
);
Tip: No need to include a <SaveButton> in the form, as <RowForm> automatically adds a column with save/cancel buttons.
If you want to avoid the edition of a column, use a <Field> component instead of an <Input> component (like the <TextField> in the example above).
Props
<RowForm> accepts the following props:
| Prop | Required | Type | Default | Description |
|---|---|---|---|---|
mutationOptions |
Optional | Object |
- | An object that can contain onSuccess and onError functions to be executed after the row has been saved and after the row has failed to be saved respectively. |
submitOnEnter |
Optional | boolean |
true |
Whether the form can be submitted by pressing the Enter key. |
transform |
Optional | function |
- | A function to transform the row before it is saved. |
Any additional props passed to <RowForm> are passed down to the underlying react-admin <Form> component. That means that you can pass e.g. defaultValues, or validate props.
import { RowForm } from '@react-admin/ra-editable-datagrid';
const ArtistForm = () => (
<RowForm defaultValues={{ firstName: 'John', name: 'Doe' }}>
<TextField source="id" disabled />
<TextInput source="name" validate={required()} />
</RowForm>
);
import { RowForm } from "@react-admin/ra-editable-datagrid";
const ArtistForm = () => (
<RowForm defaultValues={{ firstName: "John", name: "Doe" }}>
<TextField source="id" disabled />
<TextInput source="name" validate={required()} />
</RowForm>
);
useEditableDatagridContext
For advanced use cases, you can use the useEditableDatagridContext hook to manage the visibility of the creation form. It returns the following callbacks:
openStandaloneCreateForm: A function to open the create form.closeStandaloneCreateForm: A function to close the create form.
For instance, the following example displays a custom message when the list is empty, and a button to open the create form:
import React from 'react';
import { Typography, Box } from '@mui/material';
import { CreateButton, List, ListActions } from 'react-admin';
import {
EditableDatagrid,
useEditableDatagridContext,
} from '@react-admin/ra-editable-datagrid';
const MyCreateButton = () => {
const { openStandaloneCreateForm } = useEditableDatagridContext();
const handleClick = () => {
openStandaloneCreateForm();
};
return (
<Box>
<Typography>No books yet</Typography>
<Typography>Do you want to add one?</Typography>
<CreateButton onClick={handleClick} label="Create the first book" />
</Box>
);
};
export const BookList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid empty={<MyCreateButton />}>
{/*...*/}
</EditableDatagrid>
</List>
);
import React from "react";
import { Typography, Box } from "@mui/material";
import { CreateButton, List, ListActions } from "react-admin";
import { EditableDatagrid, useEditableDatagridContext } from "@react-admin/ra-editable-datagrid";
const MyCreateButton = () => {
const { openStandaloneCreateForm } = useEditableDatagridContext();
const handleClick = () => {
openStandaloneCreateForm();
};
return (
<Box>
<Typography>No books yet</Typography>
<Typography>Do you want to add one?</Typography>
<CreateButton onClick={handleClick} label="Create the first book" />
</Box>
);
};
export const BookList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
<EditableDatagrid empty={<MyCreateButton />}>{/*...*/}</EditableDatagrid>
</List>
);
Feel free to visit the dedicated stories to see more examples.
Recipes
Using Inside a <ReferenceManyField>
You can use <EditableDatagrid> inside a <ReferenceManyField>. The only difference with its usage in a <List> is that you have to initialize the foreign key in the creation form using the defaultValues prop:
import {
DateField,
DateInput,
Edit,
NumberField,
NumberInput,
ReferenceManyField,
required,
SimpleForm,
TextField,
TextInput,
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
import { EditableDatagrid, RowForm } from '@react-admin/ra-editable-datagrid';
const OrderEdit = () => (
<Edit>
<SimpleForm>
<ReferenceManyField
fullWidth
label="Products"
reference="products"
target="order_id"
>
<EditableDatagrid
mutationMode="undoable"
createForm={<ProductForm />}
editForm={<ProductForm />}
rowClick="edit"
>
<TextField source="id" />
<TextField source="name" />
<NumberField source="price" />
<DateField source="available_since" />
</EditableDatagrid>
</ReferenceManyField>
<DateInput source="purchase_date" />
</SimpleForm>
</Edit>
);
const ProductForm = () => {
const { getValues } = useFormContext();
return (
<RowForm defaultValues={{ order_id: getValues('id') }}>
<TextInput source="id" disabled />
<TextInput source="name" validate={required()} />
<NumberInput source="price" validate={required()} />
<DateInput source="available_since" validate={required()} />
</RowForm>
);
};
import {
DateField,
DateInput,
Edit,
NumberField,
NumberInput,
ReferenceManyField,
required,
SimpleForm,
TextField,
TextInput,
} from "react-admin";
import { useFormContext } from "react-hook-form";
import { EditableDatagrid, RowForm } from "@react-admin/ra-editable-datagrid";
const OrderEdit = () => (
<Edit>
<SimpleForm>
<ReferenceManyField fullWidth label="Products" reference="products" target="order_id">
<EditableDatagrid
mutationMode="undoable"
createForm={<ProductForm />}
editForm={<ProductForm />}
rowClick="edit"
>
<TextField source="id" />
<TextField source="name" />
<NumberField source="price" />
<DateField source="available_since" />
</EditableDatagrid>
</ReferenceManyField>
<DateInput source="purchase_date" />
</SimpleForm>
</Edit>
);
const ProductForm = () => {
const { getValues } = useFormContext();
return (
<RowForm defaultValues={{ order_id: getValues("id") }}>
<TextInput source="id" disabled />
<TextInput source="name" validate={required()} />
<NumberInput source="price" validate={required()} />
<DateInput source="available_since" validate={required()} />
</RowForm>
);
};
In these examples, the same form component is used in createForm and editForm, but you can pass different forms (e.g. if some fields can be set at creation but not changed afterward).
Tip: To edit a one-to-many relationship, you can also use the <ReferenceManyInput> component.
Providing Custom Side Effects
You can provide your own side effects in response to successful or failed save and delete actions, by passing functions to the onSuccess or onError inside the mutationOptions prop:
import { RowForm, useRowContext } from '@react-admin/ra-editable-datagrid';
const ArtistEditionForm = () => {
const notify = useNotify();
const { close } = useRowContext();
const handleSuccess = response => {
notify(
`Artist ${response.name} ${response.firstName} has been updated`
);
close();
};
return (
<RowForm mutationOptions={{ onSuccess: handleSuccess }}>
{/*...*/}
</RowForm>
);
};
const ArtistCreationForm = () => {
const notify = useNotify();
const { close } = useRowContext();
const handleSuccess = response => {
notify(`Artist ${response.name} ${response.firstName} has been added`);
close();
};
return (
<RowForm mutationOptions={{ onSuccess: handleSuccess }}>
{/*...*/}
</RowForm>
);
};
import { RowForm, useRowContext } from "@react-admin/ra-editable-datagrid";
const ArtistEditionForm = () => {
const notify = useNotify();
const { close } = useRowContext();
const handleSuccess = (response) => {
notify(`Artist ${response.name} ${response.firstName} has been updated`);
close();
};
return <RowForm mutationOptions={{ onSuccess: handleSuccess }}>{/*...*/}</RowForm>;
};
const ArtistCreationForm = () => {
const notify = useNotify();
const { close } = useRowContext();
const handleSuccess = (response) => {
notify(`Artist ${response.name} ${response.firstName} has been added`);
close();
};
return <RowForm mutationOptions={{ onSuccess: handleSuccess }}>{/*...*/}</RowForm>;
};
Note that we provide an additional side effects hook: useRowContext allows you to close the form.
Tip: If you use useNotify inside an onSuccess side effect for an Edit form in addition to the <EditableDatagrid mutationMode="undoable"> prop, you will need to set the notification as undoable for the changes to take effects. Also, note that, on undoable forms, the onSuccess side effect will be called immediately, without any response argument.
const handleSuccess = () => {
notify('Artist has been updated', { type: 'info', undoable: true });
close();
};
const handleSuccess = () => {
notify("Artist has been updated", { type: "info", undoable: true });
close();
};
Besides, the <RowForm> also accepts a function for its transform prop allowing you to alter the data before sending it to the dataProvider:
import { TextInput, DateInput, SelectInput } from 'react-admin';
import { RowForm } from '@react-admin/ra-editable-datagrid';
const ArtistCreateForm = () => {
const handleTransform = data => {
return {
...data,
fullName: `${data.firstName} ${data.name}`,
};
};
return (
<RowForm transform={handleTransform}>
<TextInput source="firstName" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
</RowForm>
);
};
import { TextInput, DateInput, SelectInput } from "react-admin";
import { RowForm } from "@react-admin/ra-editable-datagrid";
const ArtistCreateForm = () => {
const handleTransform = (data) => {
return {
...data,
fullName: `${data.firstName} ${data.name}`,
};
};
return (
<RowForm transform={handleTransform}>
<TextInput source="firstName" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="prof" label="Profession" choices={professionChoices} />
</RowForm>
);
};
Adding A meta Prop To All Mutations
Just like with <Datagrid>, if you'd like to add a meta prop to all the dataProvider calls, you will need to provide custom mutationOptions at all the places where mutations occur:
- the
createForm - the
editForm - the
<DeleteRowButton>
Here is a complete example:
import {
TextInput,
DateInput,
SelectInput,
TextField,
DateField,
SelectField,
required,
List,
ListActions,
} from 'react-admin';
import {
EditableDatagrid,
RowForm,
RowFormProps,
EditRowButton,
DeleteRowButton,
} from '@react-admin/ra-editable-datagrid';
const ArtistForm = ({ meta }) => (
<RowForm
defaultValues={{ firstName: 'John', name: 'Doe' }}
mutationOptions={{ meta }}
>
<TextField source="id" />
<TextInput source="firstName" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="Born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
</RowForm>
);
const ArtistListWithMeta = () => {
const meta = { foo: 'bar' };
return (
<List
actions={<ListActions hasCreate />}
sort={{ field: 'id', order: 'DESC' }}
empty={false}
>
<EditableDatagrid
createForm={<ArtistForm meta={meta} />}
editForm={<ArtistForm meta={meta} />}
rowClick="edit"
actions={
<>
<EditRowButton />
<DeleteRowButton mutationOptions={{ meta }} />
</>
}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="name" />
<DateField source="dob" label="Born" />
<SelectField
source="prof"
label="Profession"
choices={professionChoices}
/>
</EditableDatagrid>
</List>
);
};
import {
TextInput,
DateInput,
SelectInput,
TextField,
DateField,
SelectField,
required,
List,
ListActions,
} from "react-admin";
import { EditableDatagrid, RowForm, EditRowButton, DeleteRowButton } from "@react-admin/ra-editable-datagrid";
const ArtistForm = ({ meta }) => (
<RowForm defaultValues={{ firstName: "John", name: "Doe" }} mutationOptions={{ meta }}>
<TextField source="id" />
<TextInput source="firstName" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="Born" validate={required()} />
<SelectInput source="prof" label="Profession" choices={professionChoices} />
</RowForm>
);
const ArtistListWithMeta = () => {
const meta = { foo: "bar" };
return (
<List actions={<ListActions hasCreate />} sort={{ field: "id", order: "DESC" }} empty={false}>
<EditableDatagrid
createForm={<ArtistForm meta={meta} />}
editForm={<ArtistForm meta={meta} />}
rowClick="edit"
actions={
<>
<EditRowButton />
<DeleteRowButton mutationOptions={{ meta }} />
</>
}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="name" />
<DateField source="dob" label="Born" />
<SelectField source="prof" label="Profession" choices={professionChoices} />
</EditableDatagrid>
</List>
);
};
Configurable
You can let end users customize what fields are displayed in the <EditableDatagrid> by using the <EditableDatagridConfigurable> component instead, together with the <RowFormConfigurable> component.
import { List, ListActions, TextField } from 'react-admin';
import {
- EditableDatagrid,
+ EditableDatagridConfigurable,
- RowForm,
+ RowFormConfigurable,
} from '@react-admin/ra-editable-datagrid';
const ArtistForm = ({ meta }) => (
- <RowForm>
+ <RowFormConfigurable>
<TextField source="id" />
<TextInput source="firstName" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="Born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
- </RowForm>
+ </RowFormConfigurable>
);
const ArtistList = () => (
<List actions={<ListActions hasCreate />} empty={false}>
- <EditableDatagrid
+ <EditableDatagridConfigurable
mutationMode="undoable"
createForm={<ArtistForm />}
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="name" />
<DateField source="dob" label="born" />
<SelectField
source="prof"
label="Profession"
choices={professionChoices}
/>
- </EditableDatagrid>
+ </EditableDatagridConfigurable>
</List>
);
When users enter the configuration mode and select the <EditableDatagrid>, they can show/hide datagrid columns. They can also use the <SelectColumnsButton>
By default, <EditableDatagridConfigurable> renders all child fields. But you can also omit some of them by passing an omit prop containing an array of field sources:
// by default, hide the id and author columns
// users can choose to show them in configuration mode
const PostList = () => (
<List>
<EditableDatagridConfigurable omit={['id', 'author']}>
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
</EditableDatagridConfigurable>
</List>
);
// by default, hide the id and author columns
// users can choose to show them in configuration mode
const PostList = () => (
<List>
<EditableDatagridConfigurable omit={["id", "author"]}>
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
</EditableDatagridConfigurable>
</List>
);
If you render more than one <EditableDatagridConfigurable> on the same page, you must pass a unique preferenceKey prop to each one:
const PostList = () => (
<List>
<EditableDatagridConfigurable preferenceKey="posts.datagrid">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
</EditableDatagridConfigurable>
</List>
);
const PostList = () => (
<List>
<EditableDatagridConfigurable preferenceKey="posts.datagrid">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
</EditableDatagridConfigurable>
</List>
);
The inspector uses the field source (or label when it's a string) to display the column name. If you use non-field children (e.g. action buttons), then it's your responsibility to wrap them in a component with a label prop, that will be used by the inspector:
const FieldWrapper = ({ children, label }) => children;
const PostList = () => (
<List>
<EditableDatagridConfigurable>
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
<FieldWrapper label="Actions">
<EditButton />
</FieldWrapper>
</EditableDatagridConfigurable>
</List>
);
const FieldWrapper = ({ children, label }) => children;
const PostList = () => (
<List>
<EditableDatagridConfigurable>
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="year" />
<FieldWrapper label="Actions">
<EditButton />
</FieldWrapper>
</EditableDatagridConfigurable>
</List>
);
<EditableDatagridConfigurable> accepts the same props as <EditableDatagrid>.
CHANGELOG
v5.2.2
2025-07-28
- Fix
<EditableDatagrid>does not work when using a basename.
v5.2.1
2025-06-18
- Fix middleware implementation does not handle optimistic cases
v5.2.0
2025-06-16
- Make components compatible with Material UI v7
v5.1.2
2025-01-10
- (fix) Fix issue with records identifiers that contain slashes
v5.1.1
2024-11-08
- (fix) Allow
<EditableDatagrid rowClick>to navigate to custom path.
v5.1.0
2024-09-18
- (feat) Add
<EditableDatagrid disableAutofocus>prop.
v5.0.0
2024-07-25
- Upgrade to react-admin v5
- Remove
PropTypes - Fix
inflectionimports - [TypeScript]: Enable strictNullChecks
v4.6.4
2024-04-11
- Fix
EditableDatagriddoes not display validation errors on pristine fields when there are custom side effects
v4.6.3
2024-04-02
- Fix
EditableDatagridexpands all rows
v4.6.2
2024-02-26
- Fix
EditableDatagriddoes not work with customListContext
v4.6.1
2023-11-21
- Remove
<DatagridAG>, as it has been moved to its own package@react-admin/ra-datagrid-ag.
v4.6.0
2023-11-14
- Make
<DatagridAG>persist columns order and size in the Store - Enable lazy loading for
<DatagridAG>to avoid including it in the bundle when not used (see related issue on ag-grid's repo) - Transpiled ESM version of the code now targets ES2020 instead of ES2015
- Remove default notification on edit success (for a better spreadsheet-like experience)
v4.5.1
2023-10-30
- Fix
<DatagridAG>defaultdefaultColDefare mutated when adefaultColDefprop is provided
v4.5.0
2023-10-20
- Introduce
<DatagridAG>
v4.4.0
2023-09-29
- Add
<EditableDatagridConfigurable>and<RowFormConfigurable>components
v4.3.2
2023-09-19
- Add support for in
<EditableDatagrid rowSx>prop
v4.3.1
2023-07-11
- Fix
<EditableDatagrid>does not forward theheaderandsizeprops to<Datagrid>
v4.3.0
2023-05-24
- Fix
<RowForm>does not passmutationOptionswith onlymeta - Fix
<DeleteRowButton>does not supportmutationOptions - Upgraded to react-admin
4.10.6
v4.2.2
2023-04-26
- Fix
<SaveRowButton>is not disabled while saving.
v4.2.1
2023-04-07
- Fix
<RowForm>doesn't call the providedtransformfunction when submitting with the Enter key
v4.2.0
2023-03-06
- Fix
<EditableDatagrid>doesn't render the table headers when creating the first row - Upgraded to react-admin
4.8.2
v4.1.4
2023-02-09
- Fix
<EditableDatagrid>doesn't render a Create button when used inside aListContextProviderand having acreateForm
v4.1.3
2023-01-31
- Fix
<EditableDatagrid>might enter a infinite render loop when empty and a customemptycomponent was provided.
v4.1.2
2023-01-05
- Fix
DeleteWithConfirmIconButtonprops: addedmutationOptions,translateOptionsand removed unusedinvalid,pristine,saving - Fix
DeleteWithConfirmIconButtonignoresonClick - Fix propTypes definition in
EditableDatagrid - Fix custom
emptycomponent is not rendered when usingEditableDatagridin list view - Fix
useEditableDatagridContextis not exported - Fix
EditableDatagridPropstype
v4.1.1
2022-11-07
- (feat) Add ability to use
useEditableDatagridContextcallbacks even when in a List view
v4.1.0
2022-09-26
- (feat) Add
EditableDatagridContextwithuseEditableDatagridContexthook, and allow customization of the create button with theemptyprop in case there is no data inside an<EditableDatagrid>
v4.0.7
2022-09-27
- (fix) Fix ability to pass
sxprop toEditableDatagrid
v4.0.6
2022-09-19
- Fix
rowClick="show"disables row edition
v4.0.5
2022-09-05
- (fix) Fix
transformis not called when nomutationOptionsare provided - Storybook: add stories to demonstrate
transform
v4.0.4
2022-08-23
- (fix) Remove
mutationModeprop from<RowForm>(mutationModeis supported on<EditableDatagrid>only) - Doc fixes
handleSuccessexample was usingresponse.datainstead ofdata- undoable notification example was wrong
- link to OSS
mutationModewas broken - one
<EditableDatagrid>example still mentionedundoableprop instead ofmutationMode
- Storybook: add stories to demonstrate
mutationModeand side effects
v4.0.3
2022-08-04
- Fix: Passing custom actions to an
<EditableDatagrid>used inside a<ReferenceManyField>no longer hide the Create button.
v4.0.2
2022-06-29
- Fix: Replace
classnameswithclsx
v4.0.1
2022-06-08
- (fix) Update peer dependencies ranges (support React 18)
v4.0.0
2022-06-07
- Upgrade to react-admin v4
Breaking changes
- The
useCreateRowContextanduseEditRowContexthooks have been replaced by a singleuseRowContext.
import {
RowForm,
- useCreateRowContext,
- useEditRowContext,
+ useRowContext,
} from '@react-admin/ra-editable-datagrid';
const ArtistEditForm = () => {
const notify = useNotify();
- const { close } = useEditRowContext();
+ const { close } = useRowContext();
const handleSuccess = response => {
notify(
`Artist ${response.name} ${response.firstName} has been updated`
);
close();
};
return (
<RowForm mutationOptions={{ onSuccess: handleSuccess }}>
// ...
</RowForm>
);
};
const ArtistCreateForm = () => {
const notify = useNotify();
- const { close } = useCreateRowContext();
+ const { close } = useRowContext();
const handleSuccess = response => {
notify(
`Artist ${response.name} ${response.firstName} has been added`
);
close();
};
return (
<RowForm mutationOptions={{ onSuccess: handleSuccess }}>
// ...
</RowForm>
);
};
- The
undoableprop has been removed, usemutationModeinstead. It accepts the following values:pessimistic,optimistic, andundoable. See react-admin documentation about mutationMode.
<EditableDatagrid
- undoable
+ mutationMode="undoable"
// ...
```
v2.1.2
2021-11-19
- (fix) Fix sanitize title prop out of EditDialog component
v2.1.1
2021-09-20
- (fix) fix error happening after clicking the create button in an empty Datagrid with expand
v2.1.0
2021-09-06
- (feat) allows to disable submit on enter with the
submitOnEnterprop onRowForm
v2.0.1
2021-08-06
- (fix) Correctly handle prop
actionswhen set tofalse
v2.0.0
2021-08-04
-
(fix) Correctly handle prop
actionswhen set tofalse -
(fix) Breaking change: Renamed
CreateResourceButtontoCreateButton -
(feat) Add support for the
mutationModeprop introduced inreact-admin@3.12.0.The
undoableprop is deprecated, usemutationModeinstead. It accepts the following values:pessimistic,optimistic, andundoable. See react-admin documentation about mutationMode. -
(feat) Add support for custom actions on
<EditableDatagrid>through theactionsprop.
For instance, you could provide your own DeleteButton with confirmation and undoable notification:
import React from 'react';
import { List, TextField } from 'react-admin';
import {
DeleteWithConfirmIconButton,
EditableDatagrid,
EditRowButton,
} from '@react-admin/ra-editable-datagrid';
import { ArtistForm } from './ArtistForm';
const CustomAction = () => (
<>
<EditRowButton />
<DeleteWithConfirmIconButton mutationMode="undoable" />
</>
);
const ArtistList = (props: ListProps) => (
<List
{...props}
hasCreate
sort={{ field: 'id', order: 'DESC' }}
empty={false}
>
<EditableDatagrid
actions={<CustomAction />}
// The mutation mode is still applied to updates
mutationMode="undoable"
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstname" />
<TextField source="name" />
</EditableDatagrid>
</List>
);
import React from "react";
import { List, TextField } from "react-admin";
import { DeleteWithConfirmIconButton, EditableDatagrid, EditRowButton } from "@react-admin/ra-editable-datagrid";
import { ArtistForm } from "./ArtistForm";
const CustomAction = () => (
<>
<EditRowButton />
<DeleteWithConfirmIconButton mutationMode="undoable" />
</>
);
const ArtistList = (props) => (
<List {...props} hasCreate sort={{ field: "id", order: "DESC" }} empty={false}>
<EditableDatagrid
actions={<CustomAction />}
// The mutation mode is still applied to updates
mutationMode="undoable"
editForm={<ArtistForm />}
>
<TextField source="id" />
<TextField source="firstname" />
<TextField source="name" />
</EditableDatagrid>
</List>
);
v1.3.5
2021-07-28
- (fix) Ensure submitting on enter does not discard validation errors
v1.3.4
2021-07-27
- (fix) Update Delete Icon buttons to use ra-core hooks
v1.3.3
2021-07-19
- (fix) Fix creating a record fails when the Datagrid is empty
v1.3.2
2021-07-16
- (fix) Fix Edit button does not respond to click
v1.3.1
2021-06-29
- (fix) Update peer dependencies ranges (support react 17)
v1.3.0
2021-06-14
- (feat) Add support for side effects on
<RowForm>.
You can now provide your own side effects in response to successful/failed update/creation in an <EditableDatagrid> by passing functions to the onSuccess or onFailure props:
import {
DateInput,
SelectInput,
TextField,
TextInput,
useNotify,
} from 'react-admin';
import {
RowForm,
RowFormProps,
useEditRowContext,
useCreateRowContext,
} from '@react-admin/ra-editable-datagrid';
const ArtistEditForm = (props: RowFormProps) => {
const notify = useNotify();
const { close } = useEditRowContext();
const handleSuccess = response => {
notify(
`Artist ${response.data.name} ${response.data.firstName} has been updated`
);
close();
};
return (
<RowForm onSuccess={handleSuccess} {...props}>
<TextField source="id" />
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
</RowForm>
);
};
const ArtistCreateForm = props => {
const notify = useNotify();
const { close } = useCreateRowContext();
const handleSuccess = response => {
notify(
`Artist ${response.data.name} ${response.data.firstName} has been added`
);
close();
};
return (
<RowForm onSuccess={handleSuccess} {...props}>
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
</RowForm>
);
};
import { DateInput, SelectInput, TextField, TextInput, useNotify } from "react-admin";
import { RowForm, useEditRowContext, useCreateRowContext } from "@react-admin/ra-editable-datagrid";
const ArtistEditForm = (props) => {
const notify = useNotify();
const { close } = useEditRowContext();
const handleSuccess = (response) => {
notify(`Artist ${response.data.name} ${response.data.firstName} has been updated`);
close();
};
return (
<RowForm onSuccess={handleSuccess} {...props}>
<TextField source="id" />
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="prof" label="Profession" choices={professionChoices} />
</RowForm>
);
};
const ArtistCreateForm = (props) => {
const notify = useNotify();
const { close } = useCreateRowContext();
const handleSuccess = (response) => {
notify(`Artist ${response.data.name} ${response.data.firstName} has been added`);
close();
};
return (
<RowForm onSuccess={handleSuccess} {...props}>
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="prof" label="Profession" choices={professionChoices} />
</RowForm>
);
};
Note that we provide two additional side effects hooks: useEditRowContext and useCloseCreateRow, which allow you to close the form.
Besides, the <RowForm> also accepts a function for its transform prop, allowing you to alter the data before sending it to the dataProvider:
const ArtistCreateForm = props => {
const handleTransform = data => {
return {
...data,
fullName: `${data.firstName} ${data.name}`,
};
};
return (
<RowForm transform={handleTransform} {...props}>
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput
source="prof"
label="Profession"
choices={professionChoices}
/>
</RowForm>
);
};
const ArtistCreateForm = (props) => {
const handleTransform = (data) => {
return {
...data,
fullName: `${data.firstName} ${data.name}`,
};
};
return (
<RowForm transform={handleTransform} {...props}>
<TextInput source="firstname" validate={required()} />
<TextInput source="name" validate={required()} />
<DateInput source="dob" label="born" validate={required()} />
<SelectInput source="prof" label="Profession" choices={professionChoices} />
</RowForm>
);
};
v1.2.0
2021-05-17
- (chore) Upgrade to react-admin 3.15
- (feat) Ensure all components with custom styles are overridable through MUI theme by providing their key (
RaEditableDatagrid,RaEditableDatagridRowandRaRowForm).
v1.1.2
2021-05-11
- Fix
<EditableDatagrid>fails when wrapped
v1.1.1
2020-11-03
- Add the
noDeleteprop to<EditableDatagrid>to disable the inline Delete button
v1.1.0
2020-10-05
- Upgrade to react-admin
3.9
v1.0.4
2020-09-30
- Update Readme
v1.0.3
2020-09-17
- (fix) Fix crash on row deletion and impossible row creation
v1.0.2
2020-09-16
- (deps) Upgrade dependencies
v1.0.1
2020-09-07
- (feat) Add support for
expand
v1.0.0
2020-08-05
- First release