MRT logoMaterial React Table

Legacy V1 Docs

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.


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

View Extra Storybook Examples