Material-Table is a powerful data table component for React built on Material-UI that provides comprehensive features including sorting, filtering, pagination, editing, exporting, and more out of the box. It offers a simple API while delivering enterprise-grade functionality, making it perfect for building complex data management interfaces. This guide walks through creating advanced, interactive data tables using Material-Table with React, covering setup, configuration, and practical implementation patterns. This is part 11 of a series on using Material-Table with React.
Prerequisites
Before you begin, ensure you have:
- Node.js version 14.0 or higher
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) with hooks support
- Basic understanding of React hooks (useState, useCallback)
- Material-UI installed (required dependency)
- Familiarity with JavaScript/TypeScript
Installation
First, install Material-UI core components (required dependency):
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
Then install Material-Table:
npm install material-table
Or install everything with yarn:
yarn add @mui/material @mui/icons-material @emotion/react @emotion/styled material-table
Or with pnpm:
pnpm add @mui/material @mui/icons-material @emotion/react @emotion/styled material-table
Your package.json should include:
{
"dependencies": {
"material-table": "^1.69.3",
"@mui/material": "^5.0.0",
"@mui/icons-material": "^5.0.0",
"@emotion/react": "^11.0.0",
"@emotion/styled": "^11.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
Material-Table requires Material-UI's theme provider. Set up your app with the Material-UI theme:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import App from './App';
const theme = createTheme();
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
</React.StrictMode>
);
First Example / Basic Usage
Let's create a basic table component. Create src/DataTable.jsx:
// src/DataTable.jsx
import React, { useState } from 'react';
import MaterialTable from 'material-table';
function DataTable() {
const [data, setData] = useState([
{ id: 1, name: 'John Doe', email: 'john@example.com', age: 28, city: 'New York' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 32, city: 'London' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', age: 45, city: 'Paris' },
{ id: 4, name: 'Alice Williams', email: 'alice@example.com', age: 29, city: 'Tokyo' }
]);
const columns = [
{ title: 'ID', field: 'id', type: 'numeric' },
{ title: 'Name', field: 'name' },
{ title: 'Email', field: 'email' },
{ title: 'Age', field: 'age', type: 'numeric' },
{ title: 'City', field: 'city' }
];
return (
<div style={{ padding: '20px' }}>
<MaterialTable
title="Employee Directory"
columns={columns}
data={data}
options={{
search: true,
paging: true,
filtering: true,
exportButton: true
}}
/>
</div>
);
}
export default DataTable;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import { Container, Typography, Box } from '@mui/material';
import DataTable from './DataTable';
import './App.css';
function App() {
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Material-Table Example
</Typography>
<DataTable />
</Box>
</Container>
);
}
export default App;
This creates a fully functional table with:
- Built-in search functionality
- Pagination
- Column filtering
- Export button (CSV, PDF)
- Sorting (click column headers)
- Material-UI styling
Understanding the Basics
Material-Table uses a column-based configuration where:
- title: Column header text
- field: Property key in your data objects
- type: Data type ('string', 'numeric', 'boolean', 'date', etc.)
- data: Array of row data objects
Key concepts:
- Columns: Define table structure and behavior
- Data: Array of objects representing rows
- Options: Enable/disable features like search, pagination, filtering
- Actions: Custom action buttons (add, edit, delete)
- Icons: Material-UI icons for actions and features
Here's an example with editable rows and actions:
// src/EditableTable.jsx
import React, { useState } from 'react';
import MaterialTable from 'material-table';
import { forwardRef } from 'react';
import AddBox from '@mui/icons-material/AddBox';
import ArrowDownward from '@mui/icons-material/ArrowDownward';
import Check from '@mui/icons-material/Check';
import ChevronLeft from '@mui/icons-material/ChevronLeft';
import ChevronRight from '@mui/icons-material/ChevronRight';
import Clear from '@mui/icons-material/Clear';
import DeleteOutline from '@mui/icons-material/DeleteOutline';
import Edit from '@mui/icons-material/Edit';
import FilterList from '@mui/icons-material/FilterList';
import FirstPage from '@mui/icons-material/FirstPage';
import LastPage from '@mui/icons-material/LastPage';
import Remove from '@mui/icons-material/Remove';
import SaveAlt from '@mui/icons-material/SaveAlt';
import Search from '@mui/icons-material/Search';
import ViewColumn from '@mui/icons-material/ViewColumn';
const tableIcons = {
Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />),
ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
};
function EditableTable() {
const [data, setData] = useState([
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999.99, stock: 15 },
{ id: 2, name: 'Mouse', category: 'Electronics', price: 29.99, stock: 8 },
{ id: 3, name: 'Keyboard', category: 'Electronics', price: 79.99, stock: 12 }
]);
const columns = [
{ title: 'ID', field: 'id', type: 'numeric', editable: 'never' },
{ title: 'Name', field: 'name' },
{ title: 'Category', field: 'category' },
{ title: 'Price', field: 'price', type: 'currency' },
{ title: 'Stock', field: 'stock', type: 'numeric' }
];
return (
<MaterialTable
title="Product Inventory"
columns={columns}
data={data}
icons={tableIcons}
editable={{
onRowAdd: (newData) =>
new Promise((resolve) => {
setTimeout(() => {
setData([...data, { ...newData, id: data.length + 1 }]);
resolve();
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve) => {
setTimeout(() => {
const dataUpdate = [...data];
const index = oldData.tableData.id;
dataUpdate[index] = newData;
setData([...dataUpdate]);
resolve();
}, 600);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
setTimeout(() => {
const dataDelete = [...data];
const index = oldData.tableData.id;
dataDelete.splice(index, 1);
setData([...dataDelete]);
resolve();
}, 600);
})
}}
options={{
actionsColumnIndex: -1,
addRowPosition: 'first'
}}
/>
);
}
export default EditableTable;
Practical Example / Building Something Real
Let's build a comprehensive employee management system:
// src/EmployeeManagement.jsx
import React, { useState } from 'react';
import MaterialTable from 'material-table';
import { forwardRef } from 'react';
import AddBox from '@mui/icons-material/AddBox';
import ArrowDownward from '@mui/icons-material/ArrowDownward';
import Check from '@mui/icons-material/Check';
import ChevronLeft from '@mui/icons-material/ChevronLeft';
import ChevronRight from '@mui/icons-material/ChevronRight';
import Clear from '@mui/icons-material/Clear';
import DeleteOutline from '@mui/icons-material/DeleteOutline';
import Edit from '@mui/icons-material/Edit';
import FilterList from '@mui/icons-material/FilterList';
import FirstPage from '@mui/icons-material/FirstPage';
import LastPage from '@mui/icons-material/LastPage';
import Remove from '@mui/icons-material/Remove';
import SaveAlt from '@mui/icons-material/SaveAlt';
import Search from '@mui/icons-material/Search';
import ViewColumn from '@mui/icons-material/ViewColumn';
const tableIcons = {
Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />),
ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
};
function EmployeeManagement() {
const [data, setData] = useState([
{
id: 1,
name: 'Sarah Johnson',
email: 'sarah@example.com',
department: 'Engineering',
salary: 95000,
startDate: '2020-01-15',
status: 'Active'
},
{
id: 2,
name: 'Michael Chen',
email: 'michael@example.com',
department: 'Marketing',
salary: 75000,
startDate: '2019-06-20',
status: 'Active'
},
{
id: 3,
name: 'Emily Davis',
email: 'emily@example.com',
department: 'Sales',
salary: 65000,
startDate: '2021-03-10',
status: 'Active'
}
]);
const columns = [
{
title: 'ID',
field: 'id',
type: 'numeric',
editable: 'never',
width: 80
},
{
title: 'Name',
field: 'name',
validate: rowData => rowData.name === undefined || rowData.name === '' ? 'Name is required' : true
},
{
title: 'Email',
field: 'email',
validate: rowData => {
if (!rowData.email) return 'Email is required';
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(rowData.email) ? true : 'Invalid email format';
}
},
{
title: 'Department',
field: 'department',
lookup: {
Engineering: 'Engineering',
Marketing: 'Marketing',
Sales: 'Sales',
HR: 'HR'
}
},
{
title: 'Salary',
field: 'salary',
type: 'currency',
currencySetting: { currencyCode: 'USD', minimumFractionDigits: 0 }
},
{
title: 'Start Date',
field: 'startDate',
type: 'date'
},
{
title: 'Status',
field: 'status',
lookup: {
Active: 'Active',
Inactive: 'Inactive'
}
}
];
return (
<div style={{ padding: '20px' }}>
<MaterialTable
title="Employee Management"
columns={columns}
data={data}
icons={tableIcons}
editable={{
onRowAdd: (newData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
// Validate required fields
if (!newData.name || !newData.email) {
reject();
return;
}
setData([...data, { ...newData, id: data.length + 1 }]);
resolve();
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (!newData.name || !newData.email) {
reject();
return;
}
const dataUpdate = [...data];
const index = oldData.tableData.id;
dataUpdate[index] = newData;
setData([...dataUpdate]);
resolve();
}, 600);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
setTimeout(() => {
const dataDelete = [...data];
const index = oldData.tableData.id;
dataDelete.splice(index, 1);
setData([...dataDelete]);
resolve();
}, 600);
})
}}
options={{
search: true,
paging: true,
filtering: true,
exportButton: true,
exportAllData: true,
grouping: true,
actionsColumnIndex: -1,
addRowPosition: 'first',
pageSize: 10,
pageSizeOptions: [5, 10, 20, 50]
}}
localization={{
body: {
emptyDataSourceMessage: 'No records to display',
addTooltip: 'Add',
deleteTooltip: 'Delete',
editTooltip: 'Edit',
filterRow: {
filterTooltip: 'Filter'
}
},
header: {
actions: 'Actions'
},
pagination: {
labelRowsSelect: 'rows',
labelRowsPerPage: 'Rows per page:',
firstAriaLabel: 'First Page',
firstTooltip: 'First Page',
previousAriaLabel: 'Previous Page',
previousTooltip: 'Previous Page',
nextAriaLabel: 'Next Page',
nextTooltip: 'Next Page',
lastAriaLabel: 'Last Page',
lastTooltip: 'Last Page'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search'
}
}}
/>
</div>
);
}
export default EmployeeManagement;
This example demonstrates:
- Full CRUD operations (Create, Read, Update, Delete)
- Data validation for required fields and email format
- Lookup fields (dropdowns) for department and status
- Currency formatting for salary
- Date field for start date
- Grouping functionality
- Export capabilities
- Custom icons
- Localization for better UX
Common Issues / Troubleshooting
Icons not displaying: Material-Table requires Material-UI icons. Make sure
@mui/icons-materialis installed and you're providing theiconsprop with properly forwarded refs.Editing not working: Ensure you've implemented the
editableprop withonRowAdd,onRowUpdate, andonRowDeletehandlers. These should return Promises.Theme issues: Make sure your app is wrapped with Material-UI's
ThemeProviderandCssBaselinecomponent.TypeScript errors: Install
@types/material-tableif using TypeScript, or ensure your column and data types match the expected interfaces.Performance with large datasets: For very large datasets, consider implementing server-side data loading using the
dataprop with a function that returns a Promise.
Next Steps
Now that you understand Material-Table:
- Explore advanced features like detail panels and row actions
- Implement server-side data loading and pagination
- Add custom cell renderers and formatters
- Learn about grouping and aggregation
- Add export functionality (Excel, PDF)
- Explore theming and customization options
- Check the official repository: https://github.com/mbrn/material-table
- Look for part 12 of this series for more advanced topics
Summary
You've learned how to set up Material-Table and create feature-rich data tables with sorting, filtering, pagination, editing, and export capabilities. The library provides comprehensive functionality out of the box, making it easy to build enterprise-grade data management interfaces with Material-UI's design system.
SEO Keywords
material-table React
Material-Table React tutorial
material-table installation
React data table Material-UI
material-table example
React table with editing
material-table CRUD
React data grid Material-UI
material-table setup
React interactive table
material-table filtering
React table component
material-table pagination
React Material-UI table
material-table getting started
Top comments (0)