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: Lesss Than

avatar

Dusty Kuvalis

$52,729
Chief Creative Technician3/20/2014
avatar

D'angelo Moen

$71,964
Forward Response Engineer3/9/2018
avatar

Devan Reinger

$72,551
Customer Intranet Consultant8/12/2020
avatar

Leonardo Langworth

$57,801
Senior Security Manager7/25/2017
avatar

Douglas Denesik

$23,792
Legacy Security Assistant4/12/2020
avatar

Jameson Mayer

$80,916
Regional Division Planner10/30/2017
avatar

Madaline Quitzon

$68,052
Corporate Paradigm Strategist1/17/2018
avatar

Wilfrid Vandervort

$85,573
Legacy Functionality Specialist8/4/2014
avatar

Chelsie Mraz

$51,062
Forward Infrastructure Representative1/6/2021
avatar

Hassie Bruen

$61,196
Human Paradigm Designer4/28/2016

Rows per page

1-10 of 128

Source Code

1import React, { FC, 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: FC = () => {
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: ({ cell, 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 <Typography>{cell.getValue<string>()}</Typography>
66 </Box>
67 ),
68 },
69 {
70 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically
71 enableClickToCopy: true,
72 header: 'Email',
73 size: 300,
74 },
75 ],
76 },
77 {
78 id: 'id',
79 header: 'Job Info',
80 columns: [
81 {
82 accessorKey: 'salary',
83 filterVariant: 'range',
84 header: 'Salary',
85 size: 200,
86 //custom conditional format and styling
87 Cell: ({ cell }) => (
88 <Box
89 sx={(theme) => ({
90 backgroundColor:
91 cell.getValue<number>() < 50_000
92 ? theme.palette.error.dark
93 : cell.getValue<number>() >= 50_000 &&
94 cell.getValue<number>() < 75_000
95 ? theme.palette.warning.dark
96 : theme.palette.success.dark,
97 borderRadius: '0.25rem',
98 color: '#fff',
99 maxWidth: '9ch',
100 p: '0.25rem',
101 })}
102 >
103 {cell.getValue<number>()?.toLocaleString?.('en-US', {
104 style: 'currency',
105 currency: 'USD',
106 minimumFractionDigits: 0,
107 maximumFractionDigits: 0,
108 })}
109 </Box>
110 ),
111 },
112 {
113 accessorKey: 'jobTitle', //hey a simple column for once
114 header: 'Job Title',
115 size: 350,
116 },
117 {
118 accessorFn: (row) => new Date(row.startDate), //convert to Date for sorting and filtering
119 id: 'startDate',
120 header: 'Start Date',
121 filterFn: 'lessThanOrEqualTo',
122 sortingFn: 'datetime',
123 Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string
124 Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup
125 //Custom Date Picker Filter from @mui/x-date-pickers
126 Filter: ({ column }) => (
127 <LocalizationProvider dateAdapter={AdapterDayjs}>
128 <DatePicker
129 onChange={(newValue) => {
130 column.setFilterValue(newValue);
131 }}
132 renderInput={(params) => (
133 <TextField
134 {...params}
135 helperText={'Filter Mode: Lesss Than'}
136 sx={{ minWidth: '120px' }}
137 variant="standard"
138 />
139 )}
140 value={column.getFilterValue()}
141 />
142 </LocalizationProvider>
143 ),
144 },
145 ],
146 },
147 ],
148 [],
149 );
150
151 return (
152 <MaterialReactTable
153 columns={columns}
154 data={data}
155 enableColumnFilterModes
156 enableColumnOrdering
157 enableGrouping
158 enablePinning
159 enableRowActions
160 enableRowSelection
161 initialState={{ showColumnFilters: true }}
162 positionToolbarAlertBanner="bottom"
163 renderDetailPanel={({ row }) => (
164 <Box
165 sx={{
166 display: 'flex',
167 justifyContent: 'space-around',
168 alignItems: 'center',
169 }}
170 >
171 <img
172 alt="avatar"
173 height={200}
174 src={row.original.avatar}
175 loading="lazy"
176 style={{ borderRadius: '50%' }}
177 />
178 <Box sx={{ textAlign: 'center' }}>
179 <Typography variant="h4">Signature Catch Phrase:</Typography>
180 <Typography variant="h1">
181 &quot;{row.original.signatureCatchPhrase}&quot;
182 </Typography>
183 </Box>
184 </Box>
185 )}
186 renderRowActionMenuItems={({ closeMenu }) => [
187 <MenuItem
188 key={0}
189 onClick={() => {
190 // View profile logic...
191 closeMenu();
192 }}
193 sx={{ m: 0 }}
194 >
195 <ListItemIcon>
196 <AccountCircle />
197 </ListItemIcon>
198 View Profile
199 </MenuItem>,
200 <MenuItem
201 key={1}
202 onClick={() => {
203 // Send email logic...
204 closeMenu();
205 }}
206 sx={{ m: 0 }}
207 >
208 <ListItemIcon>
209 <Send />
210 </ListItemIcon>
211 Send Email
212 </MenuItem>,
213 ]}
214 renderTopToolbarCustomActions={({ table }) => {
215 const handleDeactivate = () => {
216 table.getSelectedRowModel().flatRows.map((row) => {
217 alert('deactivating ' + row.getValue('name'));
218 });
219 };
220
221 const handleActivate = () => {
222 table.getSelectedRowModel().flatRows.map((row) => {
223 alert('activating ' + row.getValue('name'));
224 });
225 };
226
227 const handleContact = () => {
228 table.getSelectedRowModel().flatRows.map((row) => {
229 alert('contact ' + row.getValue('name'));
230 });
231 };
232
233 return (
234 <div style={{ display: 'flex', gap: '0.5rem' }}>
235 <Button
236 color="error"
237 disabled={table.getSelectedRowModel().flatRows.length === 0}
238 onClick={handleDeactivate}
239 variant="contained"
240 >
241 Deactivate
242 </Button>
243 <Button
244 color="success"
245 disabled={table.getSelectedRowModel().flatRows.length === 0}
246 onClick={handleActivate}
247 variant="contained"
248 >
249 Activate
250 </Button>
251 <Button
252 color="info"
253 disabled={table.getSelectedRowModel().flatRows.length === 0}
254 onClick={handleContact}
255 variant="contained"
256 >
257 Contact
258 </Button>
259 </div>
260 );
261 }}
262 />
263 );
264};
265
266export default Example;
267

View Extra Storybook Examples