These libraries address the complex challenge of rendering, filtering, sorting, and managing large datasets within React applications. They range from unstyled, headless logic hooks that provide maximum flexibility to fully-featured, opinionated admin frameworks that handle entire CRUD workflows out of the box. Selecting the right tool depends on whether you need a simple grid component, a highly customizable table with your own design system, or a complete backend-connected administration panel.
Building interfaces that handle large datasets requires making a critical architectural decision: do you want a pre-built component that works out of the box, or a headless logic layer that gives you total control? The packages material-table, mui-datatables, ra-ui-materialui, react-admin, and react-table represent the full spectrum of these choices. Let's break down how they solve common engineering problems.
Before discussing features, we must address the status of material-table. This package is deprecated and no longer receives security updates or bug fixes. It relies on outdated versions of Material-UI (v3/v4), which creates compatibility issues with modern React ecosystems.
Recommendation: Do not start new projects with material-table. If you are maintaining a legacy system using it, plan a migration to mui-datatables or react-table immediately.
// material-table: DEPRECATED - Do not use in new code
import MaterialTable from 'material-table';
// This pattern is now risky due to lack of maintenance
export default function LegacyTable() {
return (
<MaterialTable
title="User List"
columns={[{ title: 'Name', field: 'name' }]}
data={[{ name: 'Alice' }]}
/>
);
}
The most significant difference between these tools is how much they dictate your UI.
mui-datatables provides a fully styled component based on Material UI. You get a working table instantly, but changing the look beyond theme colors can be difficult.
// mui-datatables: Opinionated styling
import MUIDataTable from "mui-datatables";
export default function StyledTable() {
const columns = ["Name", "Role"];
const data = [["Alice", "Dev"], ["Bob", "Designer"]];
return (
<MUIDataTable
title={"Employee List"}
data={data}
columns={columns}
options={{ filter: true, search: true }}
/>
);
}
react-table is "headless." It provides the logic (sorting, pagination) but renders no HTML. You must write the JSX for the table, rows, and cells yourself. This allows perfect integration with any CSS framework.
// react-table: Headless logic
import { useTable } from 'react-table';
export default function CustomTable({ columns, data }) {
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ columns, data });
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
))}
</tr>
);
})}
</tbody>
</table>
);
}
react-admin (which uses ra-ui-materialui internally) provides a complete application shell. You don't just get a table; you get a sidebar, app bar, and layout system automatically.
// react-admin: Full application shell
import { List, Datagrid, TextField, EmailField } from 'react-admin';
export const UserList = () => (
<List>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="name" />
<EmailField source="email" />
</Datagrid>
</List>
);
Handling server-side pagination, sorting, and filtering is a common bottleneck. These packages handle it differently.
mui-datatables supports server-side processing via callback props. You manage the state externally and pass it back to the component.
// mui-datatables: Server-side callbacks
const handleTableChange = (action, tableData) => {
if (action === 'sort') {
// Manually fetch new data based on tableData.sortOrder
fetchSortedData(tableData.sortOrder);
}
};
<MUIDataTable
data={serverData}
options={{ serverSide: true, onTableChange: handleTableChange }}
/>
react-table requires you to manually implement the state machine for server-side interactions using hooks like usePagination and useSortBy, giving you granular control over the request lifecycle.
// react-table: Manual server-side logic
const { state, gotoPage, setPageSize } = useTable(
{
columns,
data,
manualPagination: true, // Tell react-table to skip internal paging
pageCount: controlledPageCount,
},
usePagination,
useSortBy
);
// You must trigger API calls when state.pageIndex changes
useEffect(() => {
fetchData({ pageIndex: state.pageIndex, pageSize: state.pageSize });
}, [fetchData, state.pageIndex, state.pageSize]);
react-admin abstracts this entirely. It uses a dataProvider interface to automatically handle filtering, sorting, and pagination without you writing the fetch logic for every table.
// react-admin: Automatic data provider integration
// The <List> component automatically calls dataProvider.getList()
// with the current filter and sort params from the URL
export const PostList = () => (
<List filters={<PostFilters />} sort={{ field: 'published_at', order: 'DESC' }}>
<Datagrid>
<TextField source="title" />
<DateField source="published_at" />
</Datagrid>
</List>
);
// In your dataProvider:
export default {
getList: (resource, params) => {
// react-admin passes { pagination, sort, filter } automatically
const query = { ...params.filter, _sort: params.sort.field, _order: params.sort.order };
return fetch(`/api/${resource}?${queryString(query)}`).then(res => res.json());
}
};
When you need to render complex content inside a cell (like charts, buttons, or nested components), the approach varies.
mui-datatables allows custom body rendering via column definitions, but you are constrained by the library's internal row structure.
// mui-datatables: Custom cell render
const columns = [
{
name: "Status",
options: {
customBodyRender: (value) => {
return <Chip label={value} color={value === 'Active' ? 'success' : 'error'} />;
}
}
}
];
react-table treats cells as standard React components. You can compose any logic you want because you own the <td> tag.
// react-table: Full component composition
const columns = [
{
Header: 'Actions',
Cell: ({ row }) => (
<div className="flex gap-2">
<EditButton id={row.original.id} />
<DeleteConfirmModal id={row.original.id} />
</div>
)
}
];
react-admin uses dedicated Field components that understand data types and formatting automatically.
// react-admin: Semantic field components
import { TextField, BooleanField, ReferenceField } from 'react-admin';
<Datagrid>
<TextField source="title" />
<BooleanField source="is_published" />
{/* Automatically fetches and displays the related user name */}
<ReferenceField source="user_id" reference="users">
<TextField source="name" />
</ReferenceField>
</Datagrid>
You need an admin panel to manage users, products, and orders within two weeks. The UI doesn't need to be unique; it just needs to work.
react-adminYou are building a public-facing analytics table that must match your brand's exact design system (custom fonts, spacing, interactive rows).
react-tableYour app already uses Material UI, and you need a quick table to display a list of logs with basic sorting and filtering.
mui-datatables| Feature | mui-datatables | react-table | react-admin | material-table |
|---|---|---|---|---|
| Type | Component | Headless Hook | Full Framework | Component (Deprecated) |
| Styling | Material UI Only | None (You build it) | Material UI Based | Material UI (Old) |
| Server Side | Callback Props | Manual State Mgmt | Auto via DataProvider | Callback Props |
| Learning Curve | Low | High | Medium (Concept-heavy) | Low |
| Best For | Quick MUI Tables | Design Systems | Full Admin Apps | Legacy Only |
The choice here is not just about a table; it's about how much infrastructure you want to build.
react-admin is unmatched. It trades flexibility for speed of delivery.react-table is the industry standard for flexibility.mui-datatables saves time.material-table for new work. The technical debt it introduces is not worth the minor convenience.By understanding these trade-offs, you can select the tool that aligns with your project's timeline and design requirements.
Avoid using material-table for new projects as it is deprecated and no longer maintained. Its reliance on older Material-UI versions creates significant technical debt and security risks. Teams currently using it should prioritize migrating to mui-datatables or react-table with a custom Material UI implementation.
Choose mui-datatables if you need a quick, ready-to-use data grid specifically for Material UI (MUI) v4 or v5 without building logic from scratch. It is ideal for standard admin dashboards where default styling is acceptable and you need features like server-side pagination and filtering with minimal configuration. However, be prepared for limited customization if your design requirements deviate significantly from the defaults.
Do not install ra-ui-materialui directly as a standalone package; it is the default UI layer bundled within the react-admin framework. You only interact with this package implicitly when building an admin interface using react-admin. If you need a full-stack admin solution with built-in data providers and auth logic, choose the parent react-admin package instead.
Select react-admin if you are building a complete B2B admin panel, internal tool, or CRM that requires rapid development of CRUD operations, authentication, and real-time data syncing. It is the best choice when you want to focus on business logic rather than UI components, as it provides a full ecosystem including routing, data fetching, and form handling. Avoid it if you only need a single table component or require a highly unique, non-standard user interface.
Opt for react-table (TanStack Table) when you need a headless, unstyled solution that gives you 100% control over the HTML structure and styling. It is the architectural choice for design systems, complex enterprise grids, or when you need to integrate with UI libraries like Chakra, AntD, or Tailwind without being locked into a specific theme. Be aware that this approach requires more initial code to implement sorting, pagination, and filtering logic yourself.
:warning: Please do not create pull requests that contains a lot of change. Because we are working on refactoring and testing. Just pull requests that fixes a bug with a few line changes.
A simple and powerful Datatable for React based on Material-UI Table with some additional features.
You can access all code examples and documentation on our site material-table.com.
To support material-table visit SUPPORT page.
Issues would be prioritized according reactions count. is:issue is:open sort:reactions-+1-desc filter would be use.
List issues according to reaction score
The minimum React version material-table supports is ^16.8.5 since material-table v1.36.1. This is due to utilising react-beautiful-dnd for drag & drop functionality which uses hooks.
If you use an older version of react we suggest to upgrade your dependencies or use material-table 1.36.0.
To install material-table with npm:
npm install material-table @material-ui/core --save
To install material-table with yarn:
yarn add material-table @material-ui/core
There are two ways to use icons in material-table either import the material icons font via html OR import material icons and use the material-table icons prop.
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
OR
Icons can be imported to be used in material-table offering more flexibility for customising the look and feel of material table over using a font library.
To install @material-ui/icons with npm:
npm install @material-ui/icons --save
To install @material-ui/icons with yarn:
yarn add @material-ui/icons
If your environment doesn't support tree-shaking, the recommended way to import the icons is the following:
import AddBox from "@material-ui/icons/AddBox";
import ArrowDownward from "@material-ui/icons/ArrowDownward";
If your environment support tree-shaking you can also import the icons this way:
import { AddBox, ArrowDownward } from "@material-ui/icons";
Note: Importing named exports in this way will result in the code for every icon being included in your project, so is not recommended unless you configure tree-shaking. It may also impact Hot Module Reload performance. Source: @material-ui/icons
Example
import { forwardRef } from 'react';
import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/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} />)
};
<MaterialTable
icons={tableIcons}
...
/>
Here is a basic example of using material-table within a react application.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import MaterialTable from "material-table";
class App extends Component {
render() {
return (
<div style={{ maxWidth: "100%" }}>
<MaterialTable
columns={[
{ title: "AdΔ±", field: "name" },
{ title: "SoyadΔ±", field: "surname" },
{ title: "DoΔum YΔ±lΔ±", field: "birthYear", type: "numeric" },
{
title: "DoΔum Yeri",
field: "birthCity",
lookup: { 34: "Δ°stanbul", 63: "ΕanlΔ±urfa" },
},
]}
data={[
{
name: "Mehmet",
surname: "Baran",
birthYear: 1987,
birthCity: 63,
},
]}
title="Demo Title"
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("react-div"));
We'd love to have your helping hand on material-table! See CONTRIBUTING.md for more information on what we're looking for and how to get started.
If you have any sort of doubt, idea or just want to talk about the project, feel free to join our chat on Gitter :)
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
This project is licensed under the terms of the MIT license.