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: Lesss Than | ||
Dusty Kuvalis | $52,729 | Chief Creative Technician | 3/20/2014 | ||||
Signature Catch Phrase:"Cross-platform disintermediate workforce" | |||||||
D'angelo Moen | $71,964 | Forward Response Engineer | 3/9/2018 | ||||
Signature Catch Phrase:"Virtual local support" | |||||||
Devan Reinger | $72,551 | Customer Intranet Consultant | 8/12/2020 | ||||
Signature Catch Phrase:"Pre-emptive composite hierarchy" | |||||||
Leonardo Langworth | $57,801 | Senior Security Manager | 7/25/2017 | ||||
Signature Catch Phrase:"Progressive real-time core" | |||||||
Douglas Denesik | $23,792 | Legacy Security Assistant | 4/12/2020 | ||||
Signature Catch Phrase:"Operative well-modulated info-mediaries" | |||||||
Jameson Mayer | $80,916 | Regional Division Planner | 10/30/2017 | ||||
Signature Catch Phrase:"Front-line intermediate firmware" | |||||||
Madaline Quitzon | $68,052 | Corporate Paradigm Strategist | 1/17/2018 | ||||
Signature Catch Phrase:"Right-sized high-level algorithm" | |||||||
Wilfrid Vandervort | $85,573 | Legacy Functionality Specialist | 8/4/2014 | ||||
Signature Catch Phrase:"Focused interactive secured line" | |||||||
Chelsie Mraz | $51,062 | Forward Infrastructure Representative | 1/6/2021 | ||||
Signature Catch Phrase:"Diverse attitude-oriented migration" | |||||||
Hassie Bruen | $61,196 | Human Paradigm Designer | 4/28/2016 | ||||
Signature Catch Phrase:"Upgradable composite methodology" |
1import React, { FC, useMemo } from 'react';23//MRT Imports4import MaterialReactTable, { MRT_ColumnDef } from 'material-react-table';56//Material-UI Imports7import {8 Box,9 Button,10 ListItemIcon,11 MenuItem,12 Typography,13 TextField,14} from '@mui/material';1516//Date Picker Imports17import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';18import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';19import { DatePicker } from '@mui/x-date-pickers/DatePicker';2021//Icons Imports22import { AccountCircle, Send } from '@mui/icons-material';2324//Mock Data25import { data } from './makeData';2627export type Employee = {28 firstName: string;29 lastName: string;30 email: string;31 jobTitle: string;32 salary: number;33 startDate: string;34 signatureCatchPhrase: string;35 avatar: string;36};3738const Example: FC = () => {39 const columns = useMemo<MRT_ColumnDef<Employee>[]>(40 () => [41 {42 id: 'employee', //id used to define `group` column43 header: 'Employee',44 columns: [45 {46 accessorFn: (row) => `${row.firstName} ${row.lastName}`, //accessorFn used to join multiple data into a single cell47 id: 'name', //id is still required when using accessorFn instead of accessorKey48 header: 'Name',49 size: 250,50 Cell: ({ cell, row }) => (51 <Box52 sx={{53 display: 'flex',54 alignItems: 'center',55 gap: '1rem',56 }}57 >58 <img59 alt="avatar"60 height={30}61 src={row.original.avatar}62 loading="lazy"63 style={{ borderRadius: '50%' }}64 />65 <Typography>{cell.getValue<string>()}</Typography>66 </Box>67 ),68 },69 {70 accessorKey: 'email', //accessorKey used to define `data` column. `id` gets set to accessorKey automatically71 enableClickToCopy: true,72 header: 'Email',73 size: 300,74 },75 ],76 },77 {78 id: 'id',79 header: 'Job Info',80 columns: [81 {82 accessorKey: 'salary',83 filterVariant: 'range',84 header: 'Salary',85 size: 200,86 //custom conditional format and styling87 Cell: ({ cell }) => (88 <Box89 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 renderInput={(params) => (133 <TextField134 {...params}135 helperText={'Filter Mode: Lesss Than'}136 sx={{ minWidth: '120px' }}137 variant="standard"138 />139 )}140 value={column.getFilterValue()}141 />142 </LocalizationProvider>143 ),144 },145 ],146 },147 ],148 [],149 );150151 return (152 <MaterialReactTable153 columns={columns}154 data={data}155 enableColumnFilterModes156 enableColumnOrdering157 enableGrouping158 enablePinning159 enableRowActions160 enableRowSelection161 initialState={{ showColumnFilters: true }}162 positionToolbarAlertBanner="bottom"163 renderDetailPanel={({ row }) => (164 <Box165 sx={{166 display: 'flex',167 justifyContent: 'space-around',168 alignItems: 'center',169 }}170 >171 <img172 alt="avatar"173 height={200}174 src={row.original.avatar}175 loading="lazy"176 style={{ borderRadius: '50%' }}177 />178 <Box sx={{ textAlign: 'center' }}>179 <Typography variant="h4">Signature Catch Phrase:</Typography>180 <Typography variant="h1">181 "{row.original.signatureCatchPhrase}"182 </Typography>183 </Box>184 </Box>185 )}186 renderRowActionMenuItems={({ closeMenu }) => [187 <MenuItem188 key={0}189 onClick={() => {190 // View profile logic...191 closeMenu();192 }}193 sx={{ m: 0 }}194 >195 <ListItemIcon>196 <AccountCircle />197 </ListItemIcon>198 View Profile199 </MenuItem>,200 <MenuItem201 key={1}202 onClick={() => {203 // Send email logic...204 closeMenu();205 }}206 sx={{ m: 0 }}207 >208 <ListItemIcon>209 <Send />210 </ListItemIcon>211 Send Email212 </MenuItem>,213 ]}214 renderTopToolbarCustomActions={({ table }) => {215 const handleDeactivate = () => {216 table.getSelectedRowModel().flatRows.map((row) => {217 alert('deactivating ' + row.getValue('name'));218 });219 };220221 const handleActivate = () => {222 table.getSelectedRowModel().flatRows.map((row) => {223 alert('activating ' + row.getValue('name'));224 });225 };226227 const handleContact = () => {228 table.getSelectedRowModel().flatRows.map((row) => {229 alert('contact ' + row.getValue('name'));230 });231 };232233 return (234 <div style={{ display: 'flex', gap: '0.5rem' }}>235 <Button236 color="error"237 disabled={table.getSelectedRowModel().flatRows.length === 0}238 onClick={handleDeactivate}239 variant="contained"240 >241 Deactivate242 </Button>243 <Button244 color="success"245 disabled={table.getSelectedRowModel().flatRows.length === 0}246 onClick={handleActivate}247 variant="contained"248 >249 Activate250 </Button>251 <Button252 color="info"253 disabled={table.getSelectedRowModel().flatRows.length === 0}254 onClick={handleContact}255 variant="contained"256 >257 Contact258 </Button>259 </div>260 );261 }}262 />263 );264};265266export default Example;267
View Extra Storybook Examples