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.
Employee | Employee | Job Info | |||||
---|---|---|---|---|---|---|---|
Email | Actions | Name | Salary | Start Date | Job Title | ||
Dusty Kuvalis | $52,729 | 3/20/2014 | Chief Creative Technician | ||||
D'angelo Moen | $71,964 | 3/9/2018 | Forward Response Engineer | ||||
Devan Reinger | $72,551 | 8/12/2020 | Customer Intranet Consultant | ||||
Leonardo Langworth | $57,801 | 7/25/2017 | Senior Security Manager | ||||
Douglas Denesik | $23,792 | 4/12/2020 | Legacy Security Assistant | ||||
Jameson Mayer | $80,916 | 10/30/2017 | Regional Division Planner | ||||
Madaline Quitzon | $68,052 | 1/17/2018 | Corporate Paradigm Strategist | ||||
Wilfrid Vandervort | $85,573 | 8/4/2014 | Legacy Functionality Specialist | ||||
Chelsie Mraz | $51,062 | 1/6/2021 | Forward Infrastructure Representative | ||||
Hassie Bruen | $61,196 | 4/28/2016 | Human Paradigm Designer | ||||
10
1import { useMemo } from 'react';23//MRT Imports4import {5 MaterialReactTable,6 useMaterialReactTable,7 type MRT_ColumnDef,8 MRT_GlobalFilterTextField,9 MRT_ToggleFiltersButton,10} from 'material-react-table';1112//Material UI Imports13import {14 Box,15 Button,16 ListItemIcon,17 MenuItem,18 Typography,19 lighten,20} from '@mui/material';2122//Icons Imports23import { AccountCircle, Send } from '@mui/icons-material';2425//Mock Data26import { data } from './makeData';2728export type Employee = {29 firstName: string;30 lastName: string;31 email: string;32 jobTitle: string;33 salary: number;34 startDate: string;35 signatureCatchPhrase: string;36 avatar: string;37};3839const Example = () => {40 const columns = useMemo<MRT_ColumnDef<Employee>[]>(41 () => [42 {43 id: 'employee', //id used to define `group` column44 header: 'Employee',45 columns: [46 {47 accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell48 id: 'name', //id is still required when using accessorFn instead of accessorKey49 header: 'Name',50 size: 250,51 Cell: ({ renderedCellValue, row }) => (52 <Box53 sx={{54 display: 'flex',55 alignItems: 'center',56 gap: '1rem',57 }}58 >59 <img60 alt="avatar"61 height={30}62 src={row.original.avatar}63 loading="lazy"64 style={{ borderRadius: '50%' }}65 />66 {/* using renderedCellValue instead of cell.getValue() preserves filter match highlighting */}67 <span>{renderedCellValue}</span>68 </Box>69 ),70 },71 {72 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically73 enableClickToCopy: true,74 filterVariant: 'autocomplete',75 header: 'Email',76 size: 300,77 },78 ],79 },80 {81 id: 'id',82 header: 'Job Info',83 columns: [84 {85 accessorKey: 'salary',86 // filterVariant: 'range', //if not using filter modes feature, use this instead of filterFn87 filterFn: 'between',88 header: 'Salary',89 size: 200,90 //custom conditional format and styling91 Cell: ({ cell }) => (92 <Box93 component="span"94 sx={(theme) => ({95 backgroundColor:96 cell.getValue<number>() < 50_00097 ? theme.palette.error.dark98 : cell.getValue<number>() >= 50_000 &&99 cell.getValue<number>() < 75_000100 ? theme.palette.warning.dark101 : theme.palette.success.dark,102 borderRadius: '0.25rem',103 color: '#fff',104 maxWidth: '9ch',105 p: '0.25rem',106 })}107 >108 {cell.getValue<number>()?.toLocaleString?.('en-US', {109 style: 'currency',110 currency: 'USD',111 minimumFractionDigits: 0,112 maximumFractionDigits: 0,113 })}114 </Box>115 ),116 },117 {118 accessorKey: 'jobTitle', //hey a simple column for once119 header: 'Job Title',120 size: 350,121 },122 {123 accessorFn: (row) => new Date(row.startDate), //convert to Date for sorting and filtering124 id: 'startDate',125 header: 'Start Date',126 filterVariant: 'date',127 filterFn: 'lessThan',128 sortingFn: 'datetime',129 Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string130 Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup131 muiFilterTextFieldProps: {132 sx: {133 minWidth: '250px',134 },135 },136 },137 ],138 },139 ],140 [],141 );142143 const table = useMaterialReactTable({144 columns,145 data, //data must be memoized or stable (useState, useMemo, defined outside of this component, etc.)146 enableColumnFilterModes: true,147 enableColumnOrdering: true,148 enableGrouping: true,149 enableColumnPinning: true,150 enableFacetedValues: true,151 enableRowActions: true,152 enableRowSelection: true,153 initialState: {154 showColumnFilters: true,155 showGlobalFilter: true,156 columnPinning: { left: ['email'], right: ['jobTitle'] },157 rowSelection: { 1: true },158 },159 paginationDisplayMode: 'pages',160 positionToolbarAlertBanner: 'bottom',161 muiSearchTextFieldProps: {162 size: 'small',163 variant: 'outlined',164 },165 muiPaginationProps: {166 color: 'secondary',167 rowsPerPageOptions: [10, 20, 30],168 shape: 'rounded',169 variant: 'outlined',170 },171 renderDetailPanel: ({ row }) => (172 <Box173 sx={{174 display: 'flex',175 justifyContent: 'space-around',176 alignItems: 'center',177 }}178 >179 <img180 alt="avatar"181 height={200}182 src={row.original.avatar}183 loading="lazy"184 style={{ borderRadius: '50%' }}185 />186 <Box sx={{ textAlign: 'center' }}>187 <Typography variant="h4">Signature Catch Phrase:</Typography>188 <Typography variant="h1">189 "{row.original.signatureCatchPhrase}"190 </Typography>191 </Box>192 </Box>193 ),194 renderRowActionMenuItems: ({ closeMenu }) => [195 <MenuItem196 key={0}197 onClick={() => {198 // View profile logic...199 closeMenu();200 }}201 sx={{ m: 0 }}202 >203 <ListItemIcon>204 <AccountCircle />205 </ListItemIcon>206 View Profile207 </MenuItem>,208 <MenuItem209 key={1}210 onClick={() => {211 // Send email logic...212 closeMenu();213 }}214 sx={{ m: 0 }}215 >216 <ListItemIcon>217 <Send />218 </ListItemIcon>219 Send Email220 </MenuItem>,221 ],222 renderTopToolbar: ({ table }) => {223 const handleDeactivate = () => {224 table.getSelectedRowModel().flatRows.map((row) => {225 alert('deactivating ' + row.getValue('name'));226 });227 };228229 const handleActivate = () => {230 table.getSelectedRowModel().flatRows.map((row) => {231 alert('activating ' + row.getValue('name'));232 });233 };234235 const handleContact = () => {236 table.getSelectedRowModel().flatRows.map((row) => {237 alert('contact ' + row.getValue('name'));238 });239 };240241 return (242 <Box243 sx={(theme) => ({244 backgroundColor: lighten(theme.palette.background.default, 0.05),245 display: 'flex',246 gap: '0.5rem',247 p: '8px',248 justifyContent: 'space-between',249 })}250 >251 <Box sx={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>252 {/* import MRT sub-components */}253 <MRT_GlobalFilterTextField table={table} />254 <MRT_ToggleFiltersButton table={table} />255 </Box>256 <Box>257 <Box sx={{ display: 'flex', gap: '0.5rem' }}>258 <Button259 color="error"260 disabled={!table.getIsSomeRowsSelected()}261 onClick={handleDeactivate}262 variant="contained"263 >264 Deactivate265 </Button>266 <Button267 color="success"268 disabled={!table.getIsSomeRowsSelected()}269 onClick={handleActivate}270 variant="contained"271 >272 Activate273 </Button>274 <Button275 color="info"276 disabled={!table.getIsSomeRowsSelected()}277 onClick={handleContact}278 variant="contained"279 >280 Contact281 </Button>282 </Box>283 </Box>284 </Box>285 );286 },287 });288289 return <MaterialReactTable table={table} />;290};291292//Date Picker Imports - these should just be in your Context Provider293import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';294import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';295296const ExampleWithLocalizationProvider = () => (297 //App.tsx or AppProviders file298 <LocalizationProvider dateAdapter={AdapterDayjs}>299 <Example />300 </LocalizationProvider>301);302303export default ExampleWithLocalizationProvider;304
View Extra Storybook Examples