Advanced Example
Here is a more advanced example showcasing Mantine React Table's many features. Features such as row selection, expanding detail panels, header groups, column ordering, column pinning, column grouping, custom column and cell renders, etc., can be seen here.
This example is still only using client-side features. If you want to see an example of how to use Mantine React Table with server side logic and remote data, check out either the Remote Data Example or the React-Query Example.
Employee | Job Info | ||||||
---|---|---|---|---|---|---|---|
Name | Email | Salary | Job Title | Start Date | Actions | ||
Joseph Hand | $57,752 | Customer Directives Architect | 12/28/2022 | ||||
Paula Kohler | $47,029 | Direct Configuration Agent | 1/4/2023 | ||||
Domenic Cassin | $55,602 | Corporate Operations Planner | 2/7/2022 | ||||
Rey Runte | $88,782 | Direct Optimization Manager | 10/2/2022 | ||||
Buck Mosciski | $95,101 | Internal Mobility Orchestrator | 11/14/2022 | ||||
Johnson Nitzsche | $96,104 | Lead Accounts Director | 8/12/2022 | ||||
Silas Hermiston | $64,532 | International Operations Consultant | 6/3/2022 | ||||
Kailey Bergstrom | $26,096 | Regional Web Planner | 10/17/2022 | ||||
Lilian Tromp | $72,692 | Central Implementation Orchestrator | 9/13/2022 | ||||
Maxine Schmidt | $89,317 | Principal Communications Orchestrator | 7/1/2022 | ||||
Rows per page
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //make sure MRT styles were imported in your app root (once)
import { useMemo } from 'react';
import {
MantineReactTable,
useMantineReactTable,
type MRT_ColumnDef,
MRT_GlobalFilterTextInput,
MRT_ToggleFiltersButton,
} from 'mantine-react-table';
import { Box, Button, Flex, Menu, Text, Title } from '@mantine/core';
import { IconUserCircle, IconSend } from '@tabler/icons-react';
import { data } from './makeData';
export type Employee = {
firstName: string;
lastName: string;
email: string;
jobTitle: string;
salary: number;
startDate: string;
signatureCatchPhrase: string;
avatar: string;
};
const Example = () => {
const columns = useMemo<MRT_ColumnDef<Employee>[]>(
() => [
{
id: 'employee', //id used to define `group` column
header: 'Employee',
columns: [
{
accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell
id: 'name', //id is still required when using accessorFn instead of accessorKey
header: 'Name',
size: 250,
filterVariant: 'autocomplete',
Cell: ({ renderedCellValue, row }) => (
<Box
style={{
display: 'flex',
alignItems: 'center',
gap: '16px',
}}
>
<img
alt="avatar"
height={30}
src={row.original.avatar}
style={{ borderRadius: '50%' }}
/>
<span>{renderedCellValue}</span>
</Box>
),
},
{
accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically
enableClickToCopy: true,
header: 'Email',
size: 300,
},
],
},
{
id: 'id',
header: 'Job Info',
columns: [
{
accessorKey: 'salary',
header: 'Salary',
size: 200,
filterVariant: 'range-slider',
mantineFilterRangeSliderProps: {
color: 'indigo',
label: (value) =>
value?.toLocaleString?.('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}),
},
//custom conditional format and styling
Cell: ({ cell }) => (
<Box
style={(theme) => ({
backgroundColor:
cell.getValue<number>() < 50_000
? theme.colors.red[9]
: cell.getValue<number>() >= 50_000 &&
cell.getValue<number>() < 75_000
? theme.colors.yellow[9]
: theme.colors.green[9],
borderRadius: '4px',
color: '#fff',
maxWidth: '9ch',
padding: '4px',
})}
>
{cell.getValue<number>()?.toLocaleString?.('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})}
</Box>
),
},
{
accessorKey: 'jobTitle', //hey a simple column for once
header: 'Job Title',
filterVariant: 'multi-select',
size: 350,
},
{
accessorFn: (row) => {
//convert to Date for sorting and filtering
const sDay = new Date(row.startDate);
sDay.setHours(0, 0, 0, 0); // remove time from date (useful if filter by equals exact date)
return sDay;
},
id: 'startDate',
header: 'Start Date',
filterVariant: 'date-range',
sortingFn: 'datetime',
enableColumnFilterModes: false, //keep this as only date-range filter with between inclusive filterFn
Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string
Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup
},
],
},
],
[],
);
const table = useMantineReactTable({
columns,
data, //must be memoized or stable (useState, useMemo, defined outside of this component, etc.)
enableColumnFilterModes: true,
enableColumnOrdering: true,
enableFacetedValues: true,
enableGrouping: true,
enableColumnPinning: true,
enableRowActions: true,
enableRowSelection: true,
initialState: {
showColumnFilters: true,
showGlobalFilter: true,
columnPinning: {
left: ['mrt-row-expand', 'mrt-row-select'],
right: ['mrt-row-actions'],
},
},
paginationDisplayMode: 'pages',
positionToolbarAlertBanner: 'bottom',
mantinePaginationProps: {
radius: 'xl',
size: 'lg',
},
mantineSearchTextInputProps: {
placeholder: 'Search Employees',
},
renderDetailPanel: ({ row }) => (
<Box
style={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
gap: '16px',
padding: '16px',
}}
>
<img
alt="avatar"
height={200}
src={row.original.avatar}
style={{ borderRadius: '50%' }}
/>
<Box style={{ textAlign: 'center' }}>
<Title>Signature Catch Phrase:</Title>
<Text>"{row.original.signatureCatchPhrase}"</Text>
</Box>
</Box>
),
renderRowActionMenuItems: () => (
<>
<Menu.Item leftSection={<IconUserCircle />}>View Profile</Menu.Item>
<Menu.Item leftSection={<IconSend />}>Send Email</Menu.Item>
</>
),
renderTopToolbar: ({ table }) => {
const handleDeactivate = () => {
table.getSelectedRowModel().flatRows.map((row) => {
alert('deactivating ' + row.getValue('name'));
});
};
const handleActivate = () => {
table.getSelectedRowModel().flatRows.map((row) => {
alert('activating ' + row.getValue('name'));
});
};
const handleContact = () => {
table.getSelectedRowModel().flatRows.map((row) => {
alert('contact ' + row.getValue('name'));
});
};
return (
<Flex p="md" justify="space-between">
<Flex gap="xs">
{/* import MRT sub-components */}
<MRT_GlobalFilterTextInput table={table} />
<MRT_ToggleFiltersButton table={table} />
</Flex>
<Flex style={{ gap: '8px' }}>
<Button
color="red"
disabled={!table.getIsSomeRowsSelected()}
onClick={handleDeactivate}
variant="filled"
>
Deactivate
</Button>
<Button
color="green"
disabled={!table.getIsSomeRowsSelected()}
onClick={handleActivate}
variant="filled"
>
Activate
</Button>
<Button
color="blue"
disabled={!table.getIsSomeRowsSelected()}
onClick={handleContact}
variant="filled"
>
Contact
</Button>
</Flex>
</Flex>
);
},
});
return <MantineReactTable table={table} />;
};
export default Example;
View Extra Storybook Examples