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 | Job Info | ||||||
---|---|---|---|---|---|---|---|
Actions | Name | Email | Salary | Job Title | Start Date | ||
Dusty Kuvalis | $52,729 | Chief Creative Technician | 3/20/2014 | ||||
D'angelo Moen | $71,964 | Forward Response Engineer | 3/9/2018 | ||||
Devan Reinger | $72,551 | Customer Intranet Consultant | 8/12/2020 | ||||
Leonardo Langworth | $57,801 | Senior Security Manager | 7/25/2017 | ||||
Douglas Denesik | $23,792 | Legacy Security Assistant | 4/12/2020 | ||||
Jameson Mayer | $80,916 | Regional Division Planner | 10/30/2017 | ||||
Madaline Quitzon | $68,052 | Corporate Paradigm Strategist | 1/17/2018 | ||||
Wilfrid Vandervort | $85,573 | Legacy Functionality Specialist | 8/4/2014 | ||||
Chelsie Mraz | $51,062 | Forward Infrastructure Representative | 1/6/2021 | ||||
Hassie Bruen | $61,196 | Human Paradigm Designer | 4/28/2016 | ||||
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,146 enableColumnFilterModes: true,147 enableColumnOrdering: true,148 enableGrouping: true,149 enableColumnPinning: true,150 enableFacetedValues: true,151 enableRowActions: true,152 enableRowSelection: true,153 initialState: { showColumnFilters: true, showGlobalFilter: true },154 paginationDisplayMode: 'pages',155 positionToolbarAlertBanner: 'bottom',156 muiPaginationProps: {157 color: 'secondary',158 rowsPerPageOptions: [10, 20, 30],159 shape: 'rounded',160 variant: 'outlined'161 },162 renderDetailPanel: ({ row }) => (163 <Box164 sx={{165 display: 'flex',166 justifyContent: 'space-around',167 alignItems: 'center',168 }}169 >170 <img171 alt="avatar"172 height={200}173 src={row.original.avatar}174 loading="lazy"175 style={{ borderRadius: '50%' }}176 />177 <Box sx={{ textAlign: 'center' }}>178 <Typography variant="h4">Signature Catch Phrase:</Typography>179 <Typography variant="h1">180 "{row.original.signatureCatchPhrase}"181 </Typography>182 </Box>183 </Box>184 ),185 renderRowActionMenuItems: ({ closeMenu }) => [186 <MenuItem187 key={0}188 onClick={() => {189 // View profile logic...190 closeMenu();191 }}192 sx={{ m: 0 }}193 >194 <ListItemIcon>195 <AccountCircle />196 </ListItemIcon>197 View Profile198 </MenuItem>,199 <MenuItem200 key={1}201 onClick={() => {202 // Send email logic...203 closeMenu();204 }}205 sx={{ m: 0 }}206 >207 <ListItemIcon>208 <Send />209 </ListItemIcon>210 Send Email211 </MenuItem>,212 ],213 renderTopToolbar: ({ table }) => {214 const handleDeactivate = () => {215 table.getSelectedRowModel().flatRows.map((row) => {216 alert('deactivating ' + row.getValue('name'));217 });218 };219220 const handleActivate = () => {221 table.getSelectedRowModel().flatRows.map((row) => {222 alert('activating ' + row.getValue('name'));223 });224 };225226 const handleContact = () => {227 table.getSelectedRowModel().flatRows.map((row) => {228 alert('contact ' + row.getValue('name'));229 });230 };231232 return (233 <Box234 sx={(theme) => ({235 backgroundColor: lighten(theme.palette.background.default, 0.04),236 display: 'flex',237 gap: '0.5rem',238 p: '8px',239 justifyContent: 'space-between',240 })}241 >242 <Box sx={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>243 {/* import MRT sub-components */}244 <MRT_GlobalFilterTextField table={table} />245 <MRT_ToggleFiltersButton table={table} />246 </Box>247 <Box sx={{ display: 'flex', gap: '0.5rem' }}>248 <Button249 color="error"250 disabled={!table.getIsSomeRowsSelected()}251 onClick={handleDeactivate}252 variant="contained"253 >254 Deactivate255 </Button>256 <Button257 color="success"258 disabled={!table.getIsSomeRowsSelected()}259 onClick={handleActivate}260 variant="contained"261 >262 Activate263 </Button>264 <Button265 color="info"266 disabled={!table.getIsSomeRowsSelected()}267 onClick={handleContact}268 variant="contained"269 >270 Contact271 </Button>272 </Box>273 </Box>274 );275 },276 });277278 return <MaterialReactTable table={table} />;279};280281//Date Picker Imports - these should just be in your Context Provider282import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';283import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';284285const ExampleWithLocalizationProvider = () => (286 //App.tsx or AppProviders file287 <LocalizationProvider dateAdapter={AdapterDayjs}>288 <Example />289 </LocalizationProvider>290);291292export default ExampleWithLocalizationProvider;293
View Extra Storybook Examples