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 | ||||||
---|---|---|---|---|---|---|---|
Name | Email | Salary | Job Title | Start Date | Actions | ||
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: {154 showColumnFilters: true,155 showGlobalFilter: true,156 columnPinning: {157 left: ['mrt-row-expand', 'mrt-row-select'],158 right: ['mrt-row-actions'],159 },160 },161 paginationDisplayMode: 'pages',162 positionToolbarAlertBanner: 'bottom',163 muiSearchTextFieldProps: {164 size: 'small',165 variant: 'outlined',166 },167 muiPaginationProps: {168 color: 'secondary',169 rowsPerPageOptions: [10, 20, 30],170 shape: 'rounded',171 variant: 'outlined',172 },173 renderDetailPanel: ({ row }) => (174 <Box175 sx={{176 alignItems: 'center',177 display: 'flex',178 justifyContent: 'space-around',179 left: '30px',180 maxWidth: '1000px',181 position: 'sticky',182 width: '100%',183 }}184 >185 <img186 alt="avatar"187 height={200}188 src={row.original.avatar}189 loading="lazy"190 style={{ borderRadius: '50%' }}191 />192 <Box sx={{ textAlign: 'center' }}>193 <Typography variant="h4">Signature Catch Phrase:</Typography>194 <Typography variant="h1">195 "{row.original.signatureCatchPhrase}"196 </Typography>197 </Box>198 </Box>199 ),200 renderRowActionMenuItems: ({ closeMenu }) => [201 <MenuItem202 key={0}203 onClick={() => {204 // View profile logic...205 closeMenu();206 }}207 sx={{ m: 0 }}208 >209 <ListItemIcon>210 <AccountCircle />211 </ListItemIcon>212 View Profile213 </MenuItem>,214 <MenuItem215 key={1}216 onClick={() => {217 // Send email logic...218 closeMenu();219 }}220 sx={{ m: 0 }}221 >222 <ListItemIcon>223 <Send />224 </ListItemIcon>225 Send Email226 </MenuItem>,227 ],228 renderTopToolbar: ({ table }) => {229 const handleDeactivate = () => {230 table.getSelectedRowModel().flatRows.map((row) => {231 alert('deactivating ' + row.getValue('name'));232 });233 };234235 const handleActivate = () => {236 table.getSelectedRowModel().flatRows.map((row) => {237 alert('activating ' + row.getValue('name'));238 });239 };240241 const handleContact = () => {242 table.getSelectedRowModel().flatRows.map((row) => {243 alert('contact ' + row.getValue('name'));244 });245 };246247 return (248 <Box249 sx={(theme) => ({250 backgroundColor: lighten(theme.palette.background.default, 0.05),251 display: 'flex',252 gap: '0.5rem',253 p: '8px',254 justifyContent: 'space-between',255 })}256 >257 <Box sx={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>258 {/* import MRT sub-components */}259 <MRT_GlobalFilterTextField table={table} />260 <MRT_ToggleFiltersButton table={table} />261 </Box>262 <Box>263 <Box sx={{ display: 'flex', gap: '0.5rem' }}>264 <Button265 color="error"266 disabled={!table.getIsSomeRowsSelected()}267 onClick={handleDeactivate}268 variant="contained"269 >270 Deactivate271 </Button>272 <Button273 color="success"274 disabled={!table.getIsSomeRowsSelected()}275 onClick={handleActivate}276 variant="contained"277 >278 Activate279 </Button>280 <Button281 color="info"282 disabled={!table.getIsSomeRowsSelected()}283 onClick={handleContact}284 variant="contained"285 >286 Contact287 </Button>288 </Box>289 </Box>290 </Box>291 );292 },293 });294295 return <MaterialReactTable table={table} />;296};297298//Date Picker Imports - these should just be in your Context Provider299import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';300import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';301302const ExampleWithLocalizationProvider = () => (303 //App.tsx or AppProviders file304 <LocalizationProvider dateAdapter={AdapterDayjs}>305 <Example />306 </LocalizationProvider>307);308309export default ExampleWithLocalizationProvider;310
View Extra Storybook Examples