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 Filter Mode: Less Than | ||
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 React, { useMemo } from 'react';23//MRT Imports4import {5 MaterialReactTable,6 useMaterialReactTable,7 type MRT_ColumnDef,8} from 'material-react-table';910//Material UI Imports11import { Box, Button, ListItemIcon, MenuItem, Typography } from '@mui/material';1213//Date Picker Imports14import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';15import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';16import { DatePicker } from '@mui/x-date-pickers/DatePicker';1718//Icons Imports19import { AccountCircle, Send } from '@mui/icons-material';2021//Mock Data22import { data } from './makeData';2324export type Employee = {25 firstName: string;26 lastName: string;27 email: string;28 jobTitle: string;29 salary: number;30 startDate: string;31 signatureCatchPhrase: string;32 avatar: string;33};3435const Example = () => {36 const columns = useMemo<MRT_ColumnDef<Employee>[]>(37 () => [38 {39 id: 'employee', //id used to define `group` column40 header: 'Employee',41 columns: [42 {43 accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell44 id: 'name', //id is still required when using accessorFn instead of accessorKey45 header: 'Name',46 size: 250,47 Cell: ({ renderedCellValue, row }) => (48 <Box49 sx={{50 display: 'flex',51 alignItems: 'center',52 gap: '1rem',53 }}54 >55 <img56 alt="avatar"57 height={30}58 src={row.original.avatar}59 loading="lazy"60 style={{ borderRadius: '50%' }}61 />62 {/* using renderedCellValue instead of cell.getValue() preserves filter match highlighting */}63 <span>{renderedCellValue}</span>64 </Box>65 ),66 },67 {68 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically69 enableClickToCopy: true,70 header: 'Email',71 size: 300,72 },73 ],74 },75 {76 id: 'id',77 header: 'Job Info',78 columns: [79 {80 accessorKey: 'salary',81 // filterVariant: 'range', //if not using filter modes feature, use this instead of filterFn82 filterFn: 'between',83 header: 'Salary',84 size: 200,85 //custom conditional format and styling86 Cell: ({ cell }) => (87 <Box88 component="span"89 sx={(theme) => ({90 backgroundColor:91 cell.getValue<number>() < 50_00092 ? theme.palette.error.dark93 : cell.getValue<number>() >= 50_000 &&94 cell.getValue<number>() < 75_00095 ? theme.palette.warning.dark96 : 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 once114 header: 'Job Title',115 size: 350,116 },117 {118 accessorFn: (row) => new Date(row.startDate), //convert to Date for sorting and filtering119 id: 'startDate',120 header: 'Start Date',121 filterFn: 'lessThanOrEqualTo',122 sortingFn: 'datetime',123 Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string124 Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup125 //Custom Date Picker Filter from @mui/x-date-pickers126 Filter: ({ column }) => (127 <LocalizationProvider dateAdapter={AdapterDayjs}>128 <DatePicker129 onChange={(newValue) => {130 column.setFilterValue(newValue);131 }}132 slotProps={{133 textField: {134 helperText: 'Filter Mode: Less Than',135 sx: { minWidth: '120px' },136 variant: 'standard',137 },138 }}139 value={column.getFilterValue()}140 />141 </LocalizationProvider>142 ),143 },144 ],145 },146 ],147 [],148 );149150 const table = useMaterialReactTable({151 columns,152 data,153 enableColumnFilterModes: true,154 enableColumnOrdering: true,155 enableGrouping: true,156 enableColumnPinning: true,157 enableRowActions: true,158 enableRowSelection: true,159 initialState: { showColumnFilters: true },160 positionToolbarAlertBanner: 'bottom',161 renderDetailPanel: ({ row }) => (162 <Box163 sx={{164 display: 'flex',165 justifyContent: 'space-around',166 alignItems: 'center',167 }}168 >169 <img170 alt="avatar"171 height={200}172 src={row.original.avatar}173 loading="lazy"174 style={{ borderRadius: '50%' }}175 />176 <Box sx={{ textAlign: 'center' }}>177 <Typography variant="h4">Signature Catch Phrase:</Typography>178 <Typography variant="h1">179 "{row.original.signatureCatchPhrase}"180 </Typography>181 </Box>182 </Box>183 ),184 renderRowActionMenuItems: ({ closeMenu }) => [185 <MenuItem186 key={0}187 onClick={() => {188 // View profile logic...189 closeMenu();190 }}191 sx={{ m: 0 }}192 >193 <ListItemIcon>194 <AccountCircle />195 </ListItemIcon>196 View Profile197 </MenuItem>,198 <MenuItem199 key={1}200 onClick={() => {201 // Send email logic...202 closeMenu();203 }}204 sx={{ m: 0 }}205 >206 <ListItemIcon>207 <Send />208 </ListItemIcon>209 Send Email210 </MenuItem>,211 ],212 renderTopToolbarCustomActions: ({ table }) => {213 const handleDeactivate = () => {214 table.getSelectedRowModel().flatRows.map((row) => {215 alert('deactivating ' + row.getValue('name'));216 });217 };218219 const handleActivate = () => {220 table.getSelectedRowModel().flatRows.map((row) => {221 alert('activating ' + row.getValue('name'));222 });223 };224225 const handleContact = () => {226 table.getSelectedRowModel().flatRows.map((row) => {227 alert('contact ' + row.getValue('name'));228 });229 };230231 return (232 <div style={{ display: 'flex', gap: '0.5rem' }}>233 <Button234 color="error"235 disabled={!table.getIsSomeRowsSelected()}236 onClick={handleDeactivate}237 variant="contained"238 >239 Deactivate240 </Button>241 <Button242 color="success"243 disabled={!table.getIsSomeRowsSelected()}244 onClick={handleActivate}245 variant="contained"246 >247 Activate248 </Button>249 <Button250 color="info"251 disabled={!table.getIsSomeRowsSelected()}252 onClick={handleContact}253 variant="contained"254 >255 Contact256 </Button>257 </div>258 );259 },260 });261262 return <MaterialReactTable table={table} />;263};264265export default Example;266
View Extra Storybook Examples