material-table vs rc-table vs react-table
Architectural Patterns for React Data Grids
material-tablerc-tablereact-tableSimilar Packages:

Architectural Patterns for React Data Grids

material-table, rc-table, and react-table represent three distinct architectural approaches to building data grids in React. material-table is a high-level, opinionated component built on top of Material-UI that provides a complete UI solution with minimal setup. rc-table is a low-level, headless rendering engine used primarily as a foundation for other libraries (like Ant Design), offering maximum flexibility but requiring significant custom implementation. react-table (specifically v8+) is a framework-agnostic hook-based library that separates logic from UI, giving developers full control over the markup while providing a robust plugin system for features like sorting, filtering, and pagination.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
material-table03,483317 kB43 months agoMIT
rc-table01,373418 kB18410 months agoMIT
react-table028,345940 kB60-MIT

Architectural Patterns for React Data Grids: material-table vs rc-table vs react-table

Building a data grid is one of the most common yet complex tasks in frontend development. You need sorting, filtering, pagination, and responsive layouts, but every project has different design requirements. The ecosystem offers three main paths: a ready-made UI component (material-table), a raw rendering engine (rc-table), and a logic-first hook system (react-table). Let's break down how they actually work in production.

🏗️ Architecture: Complete Widget vs Raw Engine vs Headless Hooks

The fundamental difference lies in what each package gives you out of the box.

material-table is a "batteries-included" component. It wraps Material-UI components to give you a working table instantly. You pass data and configuration, and it renders everything.

// material-table: Complete UI component
import MaterialTable from 'material-table';

function UsersTable() {
  return (
    <MaterialTable
      title="User List"
      columns={[
        { title: 'Name', field: 'name' },
        { title: 'Surname', field: 'surname' }
      ]}
      data={[
        { name: 'John', surname: 'Doe' },
        { name: 'Jane', surname: 'Smith' }
      ]}
      options={{ paging: true, sorting: true }}
    />
  );
}

rc-table is a low-level rendering engine. It handles the <table> tag, rows, and cells, but it doesn't give you a search bar, pagination controls, or even default styling. You must build the UI around it.

// rc-table: Raw rendering engine
import Table from 'rc-table';
import 'rc-table/assets/index.css'; // Basic structural CSS only

function UsersTable() {
  const columns = [
    { title: 'Name', dataIndex: 'name', key: 'name' },
    { title: 'Surname', dataIndex: 'surname', key: 'surname' }
  ];

  const data = [
    { key: 1, name: 'John', surname: 'Doe' },
    { key: 2, name: 'Jane', surname: 'Smith' }
  ];

  // You must build your own pagination and toolbar
  return (
    <div>
      <h3>User List</h3>
      <Table columns={columns} data={data} />
    </div>
  );
}

react-table provides logic via hooks. It doesn't render any HTML. You use its state and functions to build your own table markup, giving you 100% control over the DOM.

// react-table: Headless logic hooks
import {
  useReactTable,
  getCoreRowModel,
  flexRender,
} from '@tanstack/react-table';

function UsersTable({ data }) {
  const columns = [
    { accessorKey: 'name', header: 'Name' },
    { accessorKey: 'surname', header: 'Surname' }
  ];

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    <table>
      <thead>
        {table.getHeaderGroups().map(headerGroup => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map(header => (
              <th key={header.id}>
                {flexRender(header.column.columnDef.header, header.getContext())}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map(row => (
          <tr key={row.id}>
            {row.getVisibleCells().map(cell => (
              <td key={cell.id}>
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

⚙️ Feature Implementation: Sorting and Filtering

How you add features like sorting reveals the trade-off between convenience and control.

material-table enables sorting via a simple flag. The logic is internal and hidden.

// material-table: Config-driven features
<MaterialTable
  columns={[
    { title: 'Name', field: 'name', sortable: true },
    { title: 'Age', field: 'age', sortable: true }
  ]}
  data={data}
  options={{ sorting: true }} // Global toggle
/>

rc-table requires you to handle the sort state and function yourself, then pass the sorted data back in. It does not manage sort state internally by default.

// rc-table: Manual state management
function SortableTable() {
  const [sortedData, setSortedData] = useState(data);

  const handleSort = (sorter) => {
    const { field, order } = sorter;
    const sorted = [...data].sort((a, b) => {
      if (a[field] < b[field]) return order === 'asc' ? -1 : 1;
      if (a[field] > b[field]) return order === 'asc' ? 1 : -1;
      return 0;
    });
    setSortedData(sorted);
  };

  const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
      sorter: (a, b) => a.name.localeCompare(b.name),
      // You must wire up the onClick to trigger handleSort
    }
  ];

  return <Table columns={columns} data={sortedData} onChange={(pagination, filters, sorter) => handleSort(sorter)} />;
}

react-table uses a plugin system. You import the sorting plugin, add it to the table instance, and then wire up the UI elements to the provided state handlers.

// react-table: Plugin-based logic
import {
  useReactTable,
  getCoreRowModel,
  getSortedRowModel,
  flexRender,
} from '@tanstack/react-table';

function SortableTable({ data }) {
  const columns = [
    {
      accessorKey: 'name',
      header: ({ column }) => (
        <button onClick={() => column.toggleSorting()}>
          Name {column.getIsSorted() ? (column.getIsSorted() === 'asc' ? '🔼' : '🔽') : ''}
        </button>
      ),
    },
  ];

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(), // Enable sorting logic
    state: {
      sorting: [{ id: 'name', desc: false }],
    },
  });

  // Render logic similar to previous example, now with sorted rows
  return <table>...</table>;
}

🎨 Styling and Customization

Your ability to change the look and feel depends on how much of the DOM the library owns.

material-table is tightly coupled to Material-UI. Changing the look often means overriding MUI theme values or using deep CSS selectors, which can break during upgrades.

// material-table: Theme overrides
<MaterialTable
  components={{
    Container: props => <div {...props} style={{ boxShadow: 'none', border: '1px solid #ddd' }} />
  }}
  options={{
    headerStyle: { backgroundColor: '#f5f5f5', fontWeight: 'bold' }
  }}
  data={data}
  columns={columns}
/>

rc-table gives you standard class names (.rc-table-cell, .rc-table-row), but you are responsible for all CSS. It works with any preprocessor or CSS-in-JS solution.

// rc-table: Custom CSS classes
<Table
  columns={columns}
  data={data}
  rowClassName={(record, index) => (index % 2 === 0 ? 'table-row-light' : 'table-row-dark')}
/>
/* Your custom stylesheet */
.table-row-light { background-color: #fff; }
.table-row-dark { background-color: #fafafa; }
.rc-table-cell { padding: 16px; border-bottom: 1px solid #eee; }

react-table allows you to write semantic HTML with any classes you want. There are no library-specific class names to override.

// react-table: Pure semantic HTML
<tr className="custom-row-highlight" style={{ backgroundColor: 'lightblue' }}>
  {row.getVisibleCells().map(cell => (
    <td className="custom-cell-padding" key={cell.id}>
      {flexRender(cell.column.columnDef.cell, cell.getContext())}
    </td>
  ))}
</tr>

⚠️ Maintenance and Ecosystem Reality

A critical architectural decision involves the long-term health of the dependency.

material-table has faced significant maintenance challenges. While widely used, it has struggled to keep pace with major releases of Material-UI (MUI v5+). Developers often encounter peer dependency warnings or broken features when upgrading MUI. For new greenfield projects, relying on a community-maintained wrapper that hasn't seen consistent updates recently poses a risk. If you need a MUI-based grid, the official @mui/x-data-grid is the supported alternative.

rc-table is maintained by the Ant Design team. It is extremely stable because it powers one of the world's most popular UI kits. It rarely breaks, but it also rarely adds high-level features. It is a safe bet for stability but requires more engineering effort.

react-table (now under the TanStack brand) is actively maintained and follows modern React patterns. It is framework-agnostic, meaning the same logic patterns apply if you ever move to Vue or Solid. It is the safest choice for long-term architectural flexibility.

📊 Summary: Key Differences

Featurematerial-tablerc-tablereact-table
TypeFull UI ComponentRendering EngineHeadless Logic Hooks
Setup TimeMinutesHours/DaysHours
StylingMUI Theme OnlyCustom CSSCustom CSS/JS
Logic ControlLow (Config only)Medium (Manual State)High (Full Control)
DependenciesMaterial-UINoneNone (React only)
Best ForInternal Tools, MVPsCustom Design SystemsComplex Enterprise Apps

💡 The Big Picture

material-table is the fast track 🚀. Use it when you need a table working today and your app already uses Material-UI. Just keep an eye on its maintenance status and have a migration plan ready.

rc-table is the foundation 🧱. Use it when you are building your own component library or need a table that looks nothing like standard Bootstrap or Material designs. It gives you the raw blocks without the opinions.

react-table is the engineer's choice 🛠️. Use it when the table is a core part of your application's value proposition. If you need complex interactions, server-side data handling, or a unique design that must be pixel-perfect, the extra code you write for react-table pays off in maintainability and control.

Final Thought: Don't choose a table library based on features alone. Choose based on how much control you need over the HTML and how much time you can spend building UI versus wiring up logic.

How to Choose: material-table vs rc-table vs react-table

  • material-table:

    Choose material-table if you are already using Material-UI (MUI) and need a fully functional data grid with sorting, filtering, and pagination out of the box. It is ideal for internal admin dashboards or MVPs where development speed is prioritized over custom design systems. However, be aware that this package has shown signs of maintenance stagnation; for long-term enterprise projects, verify its compatibility with your MUI version or consider migrating to MUI's official X-DataGrid if stability is critical.

  • rc-table:

    Choose rc-table if you are building a custom design system from scratch and need a lightweight, dependency-free rendering engine. It is the best choice for library authors who need to construct their own table components without being locked into a specific UI framework. Avoid this for standard application development unless you enjoy re-implementing basic UI behaviors like row hovering, column resizing, and sticky headers manually.

  • react-table:

    Choose react-table if you require complete control over the HTML structure and styling while still needing powerful state management for complex features like server-side pagination, grouped rows, or editable cells. It is the industry standard for custom tables where the design does not match existing component libraries. Be prepared to write more boilerplate code to connect the hooks to your DOM elements, as it provides logic but no visual components.

README for material-table

: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.



material-table

material-table

A simple and powerful Datatable for React based on Material-UI Table with some additional features.

Build Status Financial Contributors on Open Collective npm package NPM Downloads Average time to resolve an issue xscode Follow on Twitter Gitter chat

Roadmap

Key features

Demo and documentation

You can access all code examples and documentation on our site material-table.com.

Support material-table

To support material-table visit SUPPORT page.

Issue Prioritizing

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

Prerequisites

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.

Installation

1.Install package

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

2.Add material icons

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.

HTML
<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>

OR

Import Material icons

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}
  ...
/>

Usage

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"));

Contributing

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 :)

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

This project is licensed under the terms of the MIT license.