MRT logoMantine React Table

Editing Feature Guide

If your tables need full CRUD functionality, you can enable editing features in Mantine React Table.
There are 4 visually distinct editing modes to choose from, whether you want to let users edit data in a modal, inline 1 row at a time, 1 cell at a time, or just always have editing enabled for every cell.

Relevant Table Options

1
'modal' | 'row' | 'custom'
'modal'
MRT Editing Docs
2
'modal' | 'row' | 'cell' | 'table' | 'custom'
'modal'
MRT Editing Docs
3
boolean | (row: MRT_Row<TData>) => boolean
MRT Editing Docs
4
ModalProps | ({ row, table }) => ModalProps
Mantine Modal Docs
5
ModalProps | ({ row, table }) => ModalProps
Mantine Modal Docs
6
SelectProps | ({ cell, column, row, table }) => SelectProps
Mantine Select Docs
7
TextInputProps | ({ cell, column, row, table }) => TextInputProps
Mantine TextInput Docs
8
({ row, table }) => void
MRT Editing Docs
9
OnChangeFn<MRT_Row<TData> | null>
10
({ exitEditingMode, row, table, values}) => Promise<void> | void
MRT Editing Docs
11
OnChangeFn<MRT_Cell<TData> | null>
12
({ row, table }) => void
MRT Editing Docs
13
OnChangeFn<MRT_Row<TData> | null>
14
({ exitEditingMode, row, table, values}) => Promise<void> | void
MRT Editing Docs
15
({ row, table, internalEditComponents }) => ReactNode
16
({ row, table, internalEditComponents }) => ReactNode

Relevant Column Options

1
'select' | 'text'
'text'
MRT Editing Docs
2
boolean | (row: MRT_Row<TData>) => boolean
3
SelectProps | ({ cell, column, row, table }) => SelectProps
Mantine Select Docs
4
TextInputProps | ({ cell, column, row, table }) => TextInputProps
Mantine TextInput API

Relevant State Options

1
MRT_Row
2
MRT_Cell
3
MRT_Row
4
boolean
false

Enable Editing

To enable editing, first you just need to set the enableEditing table option to true.
const table = useMantineReactTable({
columns,
data,
enableEditing: true,
});
However, this is just the start. You will need to hook up logic and event listeners, but it depends on which editing mode you want to use.

Editing Modes

Mantine React Table has 4 supported editing modes: "modal" (default), "row", "cell" and "table". You can specify which editing mode you want to use by passing the editDisplayMode prop.
The "modal" editing mode opens up a dialog where the user can edit data for 1 row at a time. No data is saved to the table until the user clicks the save button. Clicking the cancel button clears out any changes that were made on that row.
An onEditingRowSave callback function prop must be provided where you will get access to the updated row data so that changes can be processed and saved. It is up to you how you handle the data. This function has a exitEditingMode parameter that must be called in order to exit editing mode upon save. The reason for this is so that you can perform validation checks before letting the modal close.
The onEditingRowSave callback function prop includes an exitEditingMode parameter that must be called in order to exit editing mode upon save. The reason for this is so that you can perform validation checks before letting the modal close.
DylanMurray261 Erdman FordEast DaphneKentucky
RaquelKohler769 Dominic GroveColumbusOhio
ErvinReinger566 Brakus InletSouth LindaWest Virginia
BrittanyMcCullough722 Emie StreamLincolnNebraska
BransonFrami32188 Larkin TurnpikeCharlestonSouth Carolina
1-5 of 5
1
import { useMemo, useState } from 'react';
2
import {
3
MantineReactTable,
4
MRT_TableOptions,
5
MRT_ColumnDef,
6
} from 'mantine-react-table';
7
import { data, type Person } from './makeData';
8
9
const Example = () => {
10
const columns = useMemo<MRT_ColumnDef<Person>[]>(
11
() => [
12
{
13
accessorKey: 'firstName',
14
header: 'First Name',
15
},
16
{
17
accessorKey: 'lastName',
18
header: 'Last Name',
19
},
20
21
{
22
accessorKey: 'address',
23
header: 'Address',
24
},
25
{
26
accessorKey: 'city',
27
header: 'City',
28
},
29
30
{
31
accessorKey: 'state',
32
header: 'State',
33
},
34
],
35
[],
36
);
37
38
const [tableData, setTableData] = useState<Person[]>(() => data);
39
40
const handleSaveRow: MRT_TableOptions<Person>['onEditingRowSave'] = async ({
41
table,
42
row,
43
values,
44
}) => {
45
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here.
46
tableData[row.index] = values;
47
//send/receive api updates here
48
setTableData([...tableData]);
49
table.setEditingRow(null); //exit editing mode
50
};
51
52
return (
53
<MantineReactTable
54
columns={columns}
55
data={tableData}
56
editDisplayMode="modal" //default
57
enableEditing
58
onEditingRowSave={handleSaveRow}
59
/>
60
);
61
};
62
63
export default Example;
1
import { useMemo, useState } from 'react';
2
import { MantineReactTable } from 'mantine-react-table';
3
import { data } from './makeData';
4
5
const Example = () => {
6
const columns = useMemo(
7
() => [
8
{
9
accessorKey: 'firstName',
10
header: 'First Name',
11
},
12
{
13
accessorKey: 'lastName',
14
header: 'Last Name',
15
},
16
17
{
18
accessorKey: 'address',
19
header: 'Address',
20
},
21
{
22
accessorKey: 'city',
23
header: 'City',
24
},
25
26
{
27
accessorKey: 'state',
28
header: 'State',
29
},
30
],
31
[],
32
);
33
34
const [tableData, setTableData] = useState(() => data);
35
36
const handleSaveRow = async ({ table, row, values }) => {
37
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here.
38
tableData[row.index] = values;
39
//send/receive api updates here
40
setTableData([...tableData]);
41
table.setEditingRow(null); //exit editing mode
42
};
43
44
return (
45
<MantineReactTable
46
columns={columns}
47
data={tableData}
48
editDisplayMode="modal" //default
49
enableEditing
50
onEditingRowSave={handleSaveRow}
51
/>
52
);
53
};
54
55
export default Example;

Row Editing Mode

The row editing mode is an inline row editing mode. When edit mode is activated, the row shows the edit components in the data cells. No data is saved to the table until the user clicks the save button. Clicking the cancel button clears out any changes that were made on that row.
You must provide an onEditingRowSave callback function prop where you will get access to the updated row data so that changes can be processed and saved. It is up to you how you handle the data. This function has a exitEditingMode parameter that must be called in order to exit editing mode upon save. The reason for this is so that you can perform validation checks before letting the modal close.
The onEditingRowSave callback function prop includes an exitEditingMode parameter that must be called in order to exit editing mode upon save. The reason for this is so that you can perform validation checks before letting the modal close.
DylanMurray261 Erdman FordEast DaphneKentucky
RaquelKohler769 Dominic GroveColumbusOhio
ErvinReinger566 Brakus InletSouth LindaWest Virginia
BrittanyMcCullough722 Emie StreamLincolnNebraska
BransonFrami32188 Larkin TurnpikeCharlestonSouth Carolina
1-5 of 5
1
import { useMemo, useState } from 'react';
2
import {
3
MantineReactTable,
4
MRT_TableOptions,
5
MRT_ColumnDef,
6
} from 'mantine-react-table';
7
import { data, type Person } from './makeData';
8
9
const Example = () => {
10
const columns = useMemo<MRT_ColumnDef<Person>[]>(
11
() => [
12
{
13
accessorKey: 'firstName',
14
header: 'First Name',
15
},
16
{
17
accessorKey: 'lastName',
18
header: 'Last Name',
19
},
20
21
{
22
accessorKey: 'address',
23
header: 'Address',
24
},
25
{
26
accessorKey: 'city',
27
header: 'City',
28
},
29
30
{
31
accessorKey: 'state',
32
header: 'State',
33
},
34
],
35
[],
36
);
37
38
const [tableData, setTableData] = useState<Person[]>(() => data);
39
40
const handleSaveRow: MRT_TableOptions<Person>['onEditingRowSave'] = async ({
41
table,
42
row,
43
values,
44
}) => {
45
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here.
46
tableData[row.index] = values;
47
//send/receive api updates here
48
setTableData([...tableData]);
49
table.setEditingRow(null); //exit editing mode
50
};
51
52
return (
53
<MantineReactTable
54
columns={columns}
55
data={tableData}
56
editDisplayMode="row"
57
enableEditing
58
onEditingRowSave={handleSaveRow}
59
/>
60
);
61
};
62
63
export default Example;
1
import { useMemo, useState } from 'react';
2
import { MantineReactTable } from 'mantine-react-table';
3
import { data } from './makeData';
4
5
const Example = () => {
6
const columns = useMemo(
7
() => [
8
{
9
accessorKey: 'firstName',
10
header: 'First Name',
11
},
12
{
13
accessorKey: 'lastName',
14
header: 'Last Name',
15
},
16
17
{
18
accessorKey: 'address',
19
header: 'Address',
20
},
21
{
22
accessorKey: 'city',
23
header: 'City',
24
},
25
26
{
27
accessorKey: 'state',
28
header: 'State',
29
},
30
],
31
[],
32
);
33
34
const [tableData, setTableData] = useState(() => data);
35
36
const handleSaveRow = async ({ table, row, values }) => {
37
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here.
38
tableData[row.index] = values;
39
//send/receive api updates here
40
setTableData([...tableData]);
41
table.setEditingRow(null); //exit editing mode
42
};
43
44
return (
45
<MantineReactTable
46
columns={columns}
47
data={tableData}
48
editDisplayMode="row"
49
enableEditing
50
onEditingRowSave={handleSaveRow}
51
/>
52
);
53
};
54
55
export default Example;

Cell Editing Mode

The cell editing mode is a bit simpler visually. Uses double-click cells to activate editing mode, but only for that cell.
Then there is a bit of work for you to do to wire up either the onBlur, onChange, etc. events yourself in order to save the table data. This can be done in the mantineEditTextInputProps table or column option.
DylanMurray261 Erdman FordEast DaphneKentucky
RaquelKohler769 Dominic GroveColumbusOhio
ErvinReinger566 Brakus InletSouth LindaWest Virginia
BrittanyMcCullough722 Emie StreamLincolnNebraska
BransonFrami32188 Larkin TurnpikeCharlestonSouth Carolina
Double-Click a Cell to Edit
1-5 of 5
1
import { useMemo, useState } from 'react';
2
import {
3
MantineReactTable,
4
MRT_Cell,
5
MRT_ColumnDef,
6
} from 'mantine-react-table';
7
import { Text } from '@mantine/core';
8
import { data, type Person } from './makeData';
9
10
const Example = () => {
11
const columns = useMemo<MRT_ColumnDef<Person>[]>(
12
() => [
13
{
14
accessorKey: 'firstName',
15
header: 'First Name',
16
},
17
{
18
accessorKey: 'lastName',
19
header: 'Last Name',
20
},
21
22
{
23
accessorKey: 'address',
24
header: 'Address',
25
},
26
{
27
accessorKey: 'city',
28
header: 'City',
29
},
30
{
31
accessorKey: 'state',
32
header: 'State',
33
},
34
],
35
[],
36
);
37
38
const [tableData, setTableData] = useState<Person[]>(() => data);
39
40
const handleSaveCell = (cell: MRT_Cell<Person>, value: any) => {
41
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here
42
tableData[cell.row.index][cell.column.id as keyof Person] = value;
43
//send/receive api updates here
44
setTableData([...tableData]); //re-render with new data
45
};
46
47
return (
48
<MantineReactTable
49
columns={columns}
50
data={tableData}
51
editDisplayMode="cell"
52
enableEditing
53
mantineEditTextInputProps={({ cell }) => ({
54
//onBlur is more efficient, but could use onChange instead
55
onBlur: (event) => {
56
handleSaveCell(cell, event.target.value);
57
},
58
})}
59
renderBottomToolbarCustomActions={() => (
60
<Text sx={{ fontStyle: 'italic', padding: '0 16px' }}>
61
Double-Click a Cell to Edit
62
</Text>
63
)}
64
/>
65
);
66
};
67
68
export default Example;
1
import { useMemo, useState } from 'react';
2
import { MantineReactTable } from 'mantine-react-table';
3
import { Text } from '@mantine/core';
4
import { data } from './makeData';
5
6
const Example = () => {
7
const columns = useMemo(
8
() => [
9
{
10
accessorKey: 'firstName',
11
header: 'First Name',
12
},
13
{
14
accessorKey: 'lastName',
15
header: 'Last Name',
16
},
17
18
{
19
accessorKey: 'address',
20
header: 'Address',
21
},
22
{
23
accessorKey: 'city',
24
header: 'City',
25
},
26
{
27
accessorKey: 'state',
28
header: 'State',
29
},
30
],
31
[],
32
);
33
34
const [tableData, setTableData] = useState(() => data);
35
36
const handleSaveCell = (cell, value) => {
37
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here
38
tableData[cell.row.index][cell.column.id] = value;
39
//send/receive api updates here
40
setTableData([...tableData]); //re-render with new data
41
};
42
43
return (
44
<MantineReactTable
45
columns={columns}
46
data={tableData}
47
editDisplayMode="cell"
48
enableEditing
49
mantineEditTextInputProps={({ cell }) => ({
50
//onBlur is more efficient, but could use onChange instead
51
onBlur: (event) => {
52
handleSaveCell(cell, event.target.value);
53
},
54
})}
55
renderBottomToolbarCustomActions={() => (
56
<Text sx={{ fontStyle: 'italic', padding: '0 16px' }}>
57
Double-Click a Cell to Edit
58
</Text>
59
)}
60
/>
61
);
62
};
63
64
export default Example;

Table Editing Mode

The table editing mode is similar to the cell editing mode, but it simply has all of the data cells in the table become editable all at once.
To save data, you must hook up either the onBlur, onChange, etc. events yourself. This can be done in the mantineEditTextInputProps table or column option.
1-5 of 5
1
import { useMemo, useState } from 'react';
2
import {
3
MantineReactTable,
4
MRT_Cell,
5
MRT_ColumnDef,
6
} from 'mantine-react-table';
7
import { data, type Person } from './makeData';
8
9
const Example = () => {
10
const columns = useMemo<MRT_ColumnDef<Person>[]>(
11
() => [
12
{
13
accessorKey: 'firstName',
14
header: 'First Name',
15
},
16
{
17
accessorKey: 'lastName',
18
header: 'Last Name',
19
},
20
21
{
22
accessorKey: 'address',
23
header: 'Address',
24
},
25
{
26
accessorKey: 'city',
27
header: 'City',
28
},
29
{
30
accessorKey: 'state',
31
header: 'State',
32
},
33
],
34
[],
35
);
36
37
const [tableData, setTableData] = useState<Person[]>(() => data);
38
39
const handleSaveCell = (cell: MRT_Cell<Person>, value: any) => {
40
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here
41
tableData[cell.row.index][cell.column.id as keyof Person] = value;
42
//send/receive api updates here
43
setTableData([...tableData]); //re-render with new data
44
};
45
46
return (
47
<MantineReactTable
48
columns={columns}
49
data={tableData}
50
editDisplayMode="table"
51
enableEditing
52
mantineEditTextInputProps={({ cell }) => ({
53
//onBlur is more efficient, but could use onChange instead
54
onBlur: (event) => {
55
handleSaveCell(cell, event.target.value);
56
},
57
variant: 'unstyled', //default for editDisplayMode="table"
58
})}
59
/>
60
);
61
};
62
63
export default Example;
1
import { useMemo, useState } from 'react';
2
import { MantineReactTable } from 'mantine-react-table';
3
import { data } from './makeData';
4
5
const Example = () => {
6
const columns = useMemo(
7
() => [
8
{
9
accessorKey: 'firstName',
10
header: 'First Name',
11
},
12
{
13
accessorKey: 'lastName',
14
header: 'Last Name',
15
},
16
17
{
18
accessorKey: 'address',
19
header: 'Address',
20
},
21
{
22
accessorKey: 'city',
23
header: 'City',
24
},
25
{
26
accessorKey: 'state',
27
header: 'State',
28
},
29
],
30
[],
31
);
32
33
const [tableData, setTableData] = useState(() => data);
34
35
const handleSaveCell = (cell, value) => {
36
//if using flat data and simple accessorKeys/ids, you can just do a simple assignment here
37
tableData[cell.row.index][cell.column.id] = value;
38
//send/receive api updates here
39
setTableData([...tableData]); //re-render with new data
40
};
41
42
return (
43
<MantineReactTable
44
columns={columns}
45
data={tableData}
46
editDisplayMode="table"
47
enableEditing
48
mantineEditTextInputProps={({ cell }) => ({
49
//onBlur is more efficient, but could use onChange instead
50
onBlur: (event) => {
51
handleSaveCell(cell, event.target.value);
52
},
53
variant: 'unstyled', //default for editDisplayMode="table"
54
})}
55
/>
56
);
57
};
58
59
export default Example;

Customizing Editing Components

You can customize both the Editing Modal and the Editing Inputs.

Customizing the Editing Modal

You can pass any Mantine Modal Props with the mantineEditRowModalProps prop.
const table = useMantineReactTable({
columns,
data,
enableEditing: true,
mantineEditRowModalProps: {
closeOnClickOutside: true,
withCloseButton: true,
},
});
You can also override all the content of the editing modal by passing a renderEditRowModalContent prop.
const table = useMantineReactTable({
columns,
data,
enableEditing: true,
renderEditRowModalContent: ({ internalEditComponents, row, table }) => (
<Stack>
<Title order={5}>My Custom Edit Modal</Title>
{internalEditComponents} {/*or map over row.getAllCells() and render your own components */}
<Flex justify="flex-end">
<MRT_EditActionButtons row={row} table={table} variant="text" />{' '}
{/*or render your own buttons */}
</Flex>
</Stack>
),
});

Customizing the Editing Inputs

You can pass any Mantine TextInput Props with the mantineEditTextInputProps prop.
const columns = [
{
accessor: 'age',
header: 'Age',
mantineEditTextInputProps: {
required: true,
variant: 'filled',
},
},
];

Add Validation to Editing Components

You can add validation to the editing components by using the mantineEditTextInputProps events. You can write your validation logic and hook it up to either the onBlur, onChange, etc. events, then set the error and helperText props accordingly.
If you are implementing validation, you may also need to use the onEditingRowCancel prop to clear the validation error state.
const [validationErrors, setValidationErrors] = useState({});
const columns = [
{
accessor: 'age',
header: 'Age',
mantineEditTextInputProps: {
error: validationErrors.age,
required: true,
type: 'number',
onChange: (event) => {
const value = event.target.value;
//validation logic
if (!value) {
setValidationErrors((prev) => ({ ...prev, age: 'Age is required' }));
} else if (value < 18) {
setValidationErrors({
...validationErrors,
age: 'Age must be 18 or older',
});
} else {
delete validationErrors.age;
setValidationErrors({ ...validationErrors });
}
},
},
},
];

Use Custom Editing Components

If you need to use a much more complicated Editing component than the built-in textfield, you can specify a custom editing component with the Edit column definition option.
const columns = [
{
accessorKey: 'email',
header: 'Email',
Edit: ({ cell, column, table }) => <Autocomplete />, //see MRT_EditCellTextInput source code for more info
},
];

Customize Actions/Edit Column

You can customize the actions column in a few different ways in the displayColumnDefOptions prop's 'mrt-row-actions' section.
const table = useMantineReactTable({
data,
columns,
displayColumnDefOptions: {
'mrt-row-actions': {
header: 'Edit', //change "Actions" to "Edit"
//use a text button instead of a icon button
Cell: ({ row, table }) => (
<Button onClick={() => table.setEditingRow(row)}>Edit Customer</Button>
),
},
},
});
You can help make these docs better! PRs are Welcome
Using Material-UI instead of Mantine?
Check out Material React Table