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 | ||||
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: { showColumnFilters: true, showGlobalFilter: true },154 paginationDisplayMode: 'pages',155 positionToolbarAlertBanner: 'bottom',156 muiSearchTextFieldProps: {157 size: 'small',158 variant: 'outlined',159 },160 muiPaginationProps: {161 color: 'secondary',162 rowsPerPageOptions: [10, 20, 30],163 shape: 'rounded',164 variant: 'outlined',165 },166 renderDetailPanel: ({ row }) => (167 <Box168 sx={{169 display: 'flex',170 justifyContent: 'space-around',171 alignItems: 'center',172 }}173 >174 <img175 alt="avatar"176 height={200}177 src={row.original.avatar}178 loading="lazy"179 style={{ borderRadius: '50%' }}180 />181 <Box sx={{ textAlign: 'center' }}>182 <Typography variant="h4">Signature Catch Phrase:</Typography>183 <Typography variant="h1">184 "{row.original.signatureCatchPhrase}"185 </Typography>186 </Box>187 </Box>188 ),189 renderRowActionMenuItems: ({ closeMenu }) => [190 <MenuItem191 key={0}192 onClick={() => {193 // View profile logic...194 closeMenu();195 }}196 sx={{ m: 0 }}197 >198 <ListItemIcon>199 <AccountCircle />200 </ListItemIcon>201 View Profile202 </MenuItem>,203 <MenuItem204 key={1}205 onClick={() => {206 // Send email logic...207 closeMenu();208 }}209 sx={{ m: 0 }}210 >211 <ListItemIcon>212 <Send />213 </ListItemIcon>214 Send Email215 </MenuItem>,216 ],217 renderTopToolbar: ({ table }) => {218 const handleDeactivate = () => {219 table.getSelectedRowModel().flatRows.map((row) => {220 alert('deactivating ' + row.getValue('name'));221 });222 };223224 const handleActivate = () => {225 table.getSelectedRowModel().flatRows.map((row) => {226 alert('activating ' + row.getValue('name'));227 });228 };229230 const handleContact = () => {231 table.getSelectedRowModel().flatRows.map((row) => {232 alert('contact ' + row.getValue('name'));233 });234 };235236 return (237 <Box238 sx={(theme) => ({239 backgroundColor: lighten(theme.palette.background.default, 0.05),240 display: 'flex',241 gap: '0.5rem',242 p: '8px',243 justifyContent: 'space-between',244 })}245 >246 <Box sx={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>247 {/* import MRT sub-components */}248 <MRT_GlobalFilterTextField table={table} />249 <MRT_ToggleFiltersButton table={table} />250 </Box>251 <Box>252 <Box sx={{ display: 'flex', gap: '0.5rem' }}>253 <Button254 color="error"255 disabled={!table.getIsSomeRowsSelected()}256 onClick={handleDeactivate}257 variant="contained"258 >259 Deactivate260 </Button>261 <Button262 color="success"263 disabled={!table.getIsSomeRowsSelected()}264 onClick={handleActivate}265 variant="contained"266 >267 Activate268 </Button>269 <Button270 color="info"271 disabled={!table.getIsSomeRowsSelected()}272 onClick={handleContact}273 variant="contained"274 >275 Contact276 </Button>277 </Box>278 </Box>279 </Box>280 );281 },282 });283284 return <MaterialReactTable table={table} />;285};286287//Date Picker Imports - these should just be in your Context Provider288import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';289import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';290291const ExampleWithLocalizationProvider = () => (292 //App.tsx or AppProviders file293 <LocalizationProvider dateAdapter={AdapterDayjs}>294 <Example />295 </LocalizationProvider>296);297298export default ExampleWithLocalizationProvider;299
View Extra Storybook Examples