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 Imports4//import MaterialReactTable, { type MRT_ColumnDef } from 'material-react-table'; //default import deprecated5import { MaterialReactTable, type MRT_ColumnDef } from 'material-react-table';67//Material UI Imports8import { Box, Button, ListItemIcon, MenuItem, Typography } from '@mui/material';910//Date Picker Imports11import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';12import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';13import { DatePicker } from '@mui/x-date-pickers/DatePicker';1415//Icons Imports16import { AccountCircle, Send } from '@mui/icons-material';1718//Mock Data19import { data } from './makeData';2021export type Employee = {22 firstName: string;23 lastName: string;24 email: string;25 jobTitle: string;26 salary: number;27 startDate: string;28 signatureCatchPhrase: string;29 avatar: string;30};3132const Example = () => {33 const columns = useMemo<MRT_ColumnDef<Employee>[]>(34 () => [35 {36 id: 'employee', //id used to define `group` column37 header: 'Employee',38 columns: [39 {40 accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell41 id: 'name', //id is still required when using accessorFn instead of accessorKey42 header: 'Name',43 size: 250,44 Cell: ({ renderedCellValue, row }) => (45 <Box46 sx={{47 display: 'flex',48 alignItems: 'center',49 gap: '1rem',50 }}51 >52 <img53 alt="avatar"54 height={30}55 src={row.original.avatar}56 loading="lazy"57 style={{ borderRadius: '50%' }}58 />59 {/* using renderedCellValue instead of cell.getValue() preserves filter match highlighting */}60 <span>{renderedCellValue}</span>61 </Box>62 ),63 },64 {65 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically66 enableClickToCopy: true,67 header: 'Email',68 size: 300,69 },70 ],71 },72 {73 id: 'id',74 header: 'Job Info',75 columns: [76 {77 accessorKey: 'salary',78 // filterVariant: 'range', //if not using filter modes feature, use this instead of filterFn79 filterFn: 'between',80 header: 'Salary',81 size: 200,82 //custom conditional format and styling83 Cell: ({ cell }) => (84 <Box85 component="span"86 sx={(theme) => ({87 backgroundColor:88 cell.getValue<number>() < 50_00089 ? theme.palette.error.dark90 : cell.getValue<number>() >= 50_000 &&91 cell.getValue<number>() < 75_00092 ? theme.palette.warning.dark93 : theme.palette.success.dark,94 borderRadius: '0.25rem',95 color: '#fff',96 maxWidth: '9ch',97 p: '0.25rem',98 })}99 >100 {cell.getValue<number>()?.toLocaleString?.('en-US', {101 style: 'currency',102 currency: 'USD',103 minimumFractionDigits: 0,104 maximumFractionDigits: 0,105 })}106 </Box>107 ),108 },109 {110 accessorKey: 'jobTitle', //hey a simple column for once111 header: 'Job Title',112 size: 350,113 },114 {115 accessorFn: (row) => new Date(row.startDate), //convert to Date for sorting and filtering116 id: 'startDate',117 header: 'Start Date',118 filterFn: 'lessThanOrEqualTo',119 sortingFn: 'datetime',120 Cell: ({ cell }) => cell.getValue<Date>()?.toLocaleDateString(), //render Date as a string121 Header: ({ column }) => <em>{column.columnDef.header}</em>, //custom header markup122 //Custom Date Picker Filter from @mui/x-date-pickers123 Filter: ({ column }) => (124 <LocalizationProvider dateAdapter={AdapterDayjs}>125 <DatePicker126 onChange={(newValue) => {127 column.setFilterValue(newValue);128 }}129 slotProps={{130 textField: {131 helperText: 'Filter Mode: Less Than',132 sx: { minWidth: '120px' },133 variant: 'standard',134 },135 }}136 value={column.getFilterValue()}137 />138 </LocalizationProvider>139 ),140 },141 ],142 },143 ],144 [],145 );146147 return (148 <MaterialReactTable149 columns={columns}150 data={data}151 enableColumnFilterModes152 enableColumnOrdering153 enableGrouping154 enablePinning155 enableRowActions156 enableRowSelection157 initialState={{ showColumnFilters: true }}158 positionToolbarAlertBanner="bottom"159 renderDetailPanel={({ row }) => (160 <Box161 sx={{162 display: 'flex',163 justifyContent: 'space-around',164 alignItems: 'center',165 }}166 >167 <img168 alt="avatar"169 height={200}170 src={row.original.avatar}171 loading="lazy"172 style={{ borderRadius: '50%' }}173 />174 <Box sx={{ textAlign: 'center' }}>175 <Typography variant="h4">Signature Catch Phrase:</Typography>176 <Typography variant="h1">177 "{row.original.signatureCatchPhrase}"178 </Typography>179 </Box>180 </Box>181 )}182 renderRowActionMenuItems={({ closeMenu }) => [183 <MenuItem184 key={0}185 onClick={() => {186 // View profile logic...187 closeMenu();188 }}189 sx={{ m: 0 }}190 >191 <ListItemIcon>192 <AccountCircle />193 </ListItemIcon>194 View Profile195 </MenuItem>,196 <MenuItem197 key={1}198 onClick={() => {199 // Send email logic...200 closeMenu();201 }}202 sx={{ m: 0 }}203 >204 <ListItemIcon>205 <Send />206 </ListItemIcon>207 Send Email208 </MenuItem>,209 ]}210 renderTopToolbarCustomActions={({ table }) => {211 const handleDeactivate = () => {212 table.getSelectedRowModel().flatRows.map((row) => {213 alert('deactivating ' + row.getValue('name'));214 });215 };216217 const handleActivate = () => {218 table.getSelectedRowModel().flatRows.map((row) => {219 alert('activating ' + row.getValue('name'));220 });221 };222223 const handleContact = () => {224 table.getSelectedRowModel().flatRows.map((row) => {225 alert('contact ' + row.getValue('name'));226 });227 };228229 return (230 <div style={{ display: 'flex', gap: '0.5rem' }}>231 <Button232 color="error"233 disabled={!table.getIsSomeRowsSelected()}234 onClick={handleDeactivate}235 variant="contained"236 >237 Deactivate238 </Button>239 <Button240 color="success"241 disabled={!table.getIsSomeRowsSelected()}242 onClick={handleActivate}243 variant="contained"244 >245 Activate246 </Button>247 <Button248 color="info"249 disabled={!table.getIsSomeRowsSelected()}250 onClick={handleContact}251 variant="contained"252 >253 Contact254 </Button>255 </div>256 );257 }}258 />259 );260};261262export default Example;263
View Extra Storybook Examples