MRT logoMaterial React Table

Advanced Example

Here is a more advanced example showcasing Material 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 Material React Table with server side logic and remote data, check out either the Remote Data Example or the React-Query Example.


Demo

Open StackblitzOpen Code SandboxOpen on GitHub

Filter Mode: Less Than

avatarDusty Kuvalis
$52,729Chief Creative Technician3/20/2014
avatarD'angelo Moen
$71,964Forward Response Engineer3/9/2018
avatarDevan Reinger
$72,551Customer Intranet Consultant8/12/2020
avatarLeonardo Langworth
$57,801Senior Security Manager7/25/2017
avatarDouglas Denesik
$23,792Legacy Security Assistant4/12/2020
avatarJameson Mayer
$80,916Regional Division Planner10/30/2017
avatarMadaline Quitzon
$68,052Corporate Paradigm Strategist1/17/2018
avatarWilfrid Vandervort
$85,573Legacy Functionality Specialist8/4/2014
avatarChelsie Mraz
$51,062Forward Infrastructure Representative1/6/2021
avatarHassie Bruen
$61,196Human Paradigm Designer4/28/2016

Rows per page

1-10 of 128

Source Code

1import React, { useMemo } from 'react';
2
3//MRT Imports
4import MaterialReactTable, { MRT_ColumnDef } from 'material-react-table';
5
6//Material-UI Imports
7import {
8 Box,
9 Button,
10 ListItemIcon,
11 MenuItem,
12 Typography,
13 TextField,
14} from '@mui/material';
15
16//Date Picker Imports
17import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
18import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
19import { DatePicker } from '@mui/x-date-pickers/DatePicker';
20
21//Icons Imports
22import { AccountCircle, Send } from '@mui/icons-material';
23
24//Mock Data
25import { data } from './makeData';
26
27export type Employee = {
28 firstName: string;
29 lastName: string;
30 email: string;
31 jobTitle: string;
32 salary: number;
33 startDate: string;
34 signatureCatchPhrase: string;
35 avatar: string;
36};
37
38const Example = () => {
39 const columns = useMemo<MRT_ColumnDef<Employee>[]>(
40 () => [
41 {
42 id: 'employee', //id used to define `group` column
43 header: 'Employee',
44 columns: [
45 {
46 accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell
47 id: 'name', //id is still required when using accessorFn instead of accessorKey
48 header: 'Name',
49 size: 250,
50 Cell: ({ renderedCellValue, row }) => (
51 <Box
52 sx={{
53 display: 'flex',
54 alignItems: 'center',
55 gap: '1rem',
56 }}
57 >
58 <img
59 alt="avatar"
60 height={30}
61 src={row.original.avatar}
62 loading="lazy"
63 style={{ borderRadius: '50%' }}
64 />
65 {/* using renderedCellValue instead of cell.getValue() preserves filter match highlighting */}
66 <span>{renderedCellValue}</span>
67 </Box>
68 ),
69 },
70 {
71 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically
72 enableClickToCopy: true,
73 header: 'Email',
74 size: 300,
75 },
76 ],
77 },
78 {
79 id: 'id',
80 header: 'Job Info',
81 columns: [
82 {
83 accessorKey: 'salary',
84 filterVariant: 'range',
85 header: 'Salary',
86 size: 200,
87 //custom conditional format and styling
88 Cell: ({ cell }) => (
89 <Box
90 component="span"
91 sx={(theme) => ({
92 backgroundColor:
93 cell.getValue<number>() < 50_000
94 ? theme.palette.error.dark
95 : cell.getValue<number>() >= 50_000 &&
96 cell.getValue<number>() < 75_000
97 ? theme.palette.warning.dark
98 : theme.palette.success.dark,
99 borderRadius: '0.25rem',
100 color: '#fff',
101 maxWidth: '9ch',
102 p: '0.25rem',
103 })}
104 >
105 {cell.getValue<number>()?.toLocaleString?.('en-US', {
106 style: 'currency',
107 currency: 'USD',
108 minimumFractionDigits: 0,
109 maximumFractionDigits: 0,
110 })}
111 </Box>
112 ),
113 },
114 {
115 accessorKey: 'jobTitle', //hey a simple column for once
116 header: 'Job Title',
117 size: 350,
118 },
119 {
120 accessorFn: (row) => new Date(row.startDate), //convert to Date for sorting and filtering
121 id: 'startDate',
122 header: 'Start Date',
123 filterFn: 'lessThanOrEqualTo',
124 sortingFn: 'datetime',
125 Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string
126 Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup
127 //Custom Date Picker Filter from @mui/x-date-pickers
128 Filter: ({ column }) => (
129 <LocalizationProvider dateAdapter={AdapterDayjs}>
130 <DatePicker
131 onChange={(newValue) => {
132 column.setFilterValue(newValue);
133 }}
134 renderInput={(params) => (
135 <TextField
136 {...params}
137 helperText={'Filter Mode: Less Than'}
138 sx={{ minWidth: '120px' }}
139 variant="standard"
140 />
141 )}
142 value={column.getFilterValue()}
143 />
144 </LocalizationProvider>
145 ),
146 },
147 ],
148 },
149 ],
150 [],
151 );
152
153 return (
154 <MaterialReactTable
155 columns={columns}
156 data={data}
157 enableColumnFilterModes
158 enableColumnOrdering
159 enableGrouping
160 enablePinning
161 enableRowActions
162 enableRowSelection
163 initialState={{ showColumnFilters: true }}
164 positionToolbarAlertBanner="bottom"
165 renderDetailPanel={({ row }) => (
166 <Box
167 sx={{
168 display: 'flex',
169 justifyContent: 'space-around',
170 alignItems: 'center',
171 }}
172 >
173 <img
174 alt="avatar"
175 height={200}
176 src={row.original.avatar}
177 loading="lazy"
178 style={{ borderRadius: '50%' }}
179 />
180 <Box sx={{ textAlign: 'center' }}>
181 <Typography variant="h4">Signature Catch Phrase:</Typography>
182 <Typography variant="h1">
183 &quot;{row.original.signatureCatchPhrase}&quot;
184 </Typography>
185 </Box>
186 </Box>
187 )}
188 renderRowActionMenuItems={({ closeMenu }) => [
189 <MenuItem
190 key={0}
191 onClick={() => {
192 // View profile logic...
193 closeMenu();
194 }}
195 sx={{ m: 0 }}
196 >
197 <ListItemIcon>
198 <AccountCircle />
199 </ListItemIcon>
200 View Profile
201 </MenuItem>,
202 <MenuItem
203 key={1}
204 onClick={() => {
205 // Send email logic...
206 closeMenu();
207 }}
208 sx={{ m: 0 }}
209 >
210 <ListItemIcon>
211 <Send />
212 </ListItemIcon>
213 Send Email
214 </MenuItem>,
215 ]}
216 renderTopToolbarCustomActions={({ table }) => {
217 const handleDeactivate = () => {
218 table.getSelectedRowModel().flatRows.map((row) => {
219 alert('deactivating ' + row.getValue('name'));
220 });
221 };
222
223 const handleActivate = () => {
224 table.getSelectedRowModel().flatRows.map((row) => {
225 alert('activating ' + row.getValue('name'));
226 });
227 };
228
229 const handleContact = () => {
230 table.getSelectedRowModel().flatRows.map((row) => {
231 alert('contact ' + row.getValue('name'));
232 });
233 };
234
235 return (
236 <div style={{ display: 'flex', gap: '0.5rem' }}>
237 <Button
238 color="error"
239 disabled={!table.getIsSomeRowsSelected()}
240 onClick={handleDeactivate}
241 variant="contained"
242 >
243 Deactivate
244 </Button>
245 <Button
246 color="success"
247 disabled={!table.getIsSomeRowsSelected()}
248 onClick={handleActivate}
249 variant="contained"
250 >
251 Activate
252 </Button>
253 <Button
254 color="info"
255 disabled={!table.getIsSomeRowsSelected()}
256 onClick={handleContact}
257 variant="contained"
258 >
259 Contact
260 </Button>
261 </div>
262 );
263 }}
264 />
265 );
266};
267
268export default Example;
269

View Extra Storybook Examples