Tables are a vital part of almost any web application, especially heavily data-driven applications.
In this tutorial, we’ll review the importance of choosing the right table component for your project and demonstrate how to create tables in React using one of the best and most feature-rich libraries available: material-table.
Here’s what we’ll cover:
.csv
and .pdf
A plain HTML table is very powerful and can be used to present data quickly. You can even add customized styling to make it your own. If you’re only concerned with presenting simple data to your end users, a plain old HTML table should be enough for you.
But plain HTML tables have some limitations, including:
Table components are designed to help you solve these and other issues associated with plain HTML tables. A good table component should have the following features built in:
The features mentioned above are commonly found in modern applications that present data in table format.
material-table is virtually the only table library that supports all of the aforementioned features.
The library is built on top of Material UI, the most popular UI library in the React ecosystem. material-table will blend in perfectly if you are already using Material UI.
With the ability to customize the look and feel of a table build with material-table, you can use it in any React project.
If you’re a visual learner, check out this video tutorial to help you get started using material-table.
No Title
In this video, we will use the material table in a React project. This is one of the most powerful table components for the ReactJS application. The Github Repo: https://github.com/Mohammad-Faisal/material-table-demo Material Table Documentation: https://material-table.com/#/docs/get-started
First, create a new project using Create React App.
npx create-react-app material-table-demo
After the project is created, go into the root folder of your project:
cd material-table-demo
And add the following dependencies to use material-table:
npm install material-table --save npm install @material-ui/core --save
Or, if you want to use yarn:
yarn add material-table yarn add @material-ui/core
That’s all you need to get started with material-table.
To render a table with material-table, you have to supply the data (an array of objects) and the name of the columns to map with the data. The columns will specify which piece of data will go in which column.
Let’s create a new file named BasicTable.jsx
and add the following code:
import MaterialTable from "material-table"; const data = [ { name: "Mohammad", surname: "Faisal", birthYear: 1995 }, { name: "Nayeem Raihan ", surname: "Shuvo", birthYear: 1994 }, ]; const columns = [ { title: "Name", field: "name" }, { title: "Surname", field: "surname" }, { title: "Birth Year", field: "birthYear", type: "numeric" }, ]; export const BasicTable = () => { return <MaterialTable title="Basic Table" columns={columns} data={data} />; };
This piece of code will render the following table:
Nice! Our data is displayed properly and we also have the default search
and pagination
functionality — and we didn’t need to write a single line of code.
But wait, something is not right. If you direct your attention to the arrows drawn on the image, you’ll notice some weird text. This is because material-table internally tries to use Material Icons, which we need to manually.
To add Material Icons to our table, we must first install the dependency inside our project.
npm install @material-ui/icons --save
Or, with yarn:
yarn add @material-ui/icons
We have added Material Icons to our project.
Now let’s say we want to import a single icon. Do we need to add the whole set of icons? Obviously not — that would make the app too heavy.
Below we’ll demonstrate the wrong and correct ways to import Material Icons to use in your material-table.
Wrong way:
import { AddBox, ArrowDownward } from "@material-ui/icons";
This will result in importing all the icons.
Correct way:
Instead, we should try to only get the specific icon:
import AddBox from "@material-ui/icons/AddBox"; import ArrowDownward from "@material-ui/icons/ArrowDownward";
Following this best practice, let’s create a special component to add all the required icons into the project and reuse that everywhere.
Create a new file named MaterialTableIcons.js
and add the following code:
import React, { 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} />), }; export default tableIcons;
The cool thing is if you don’t like the icons from the material-icons
, you can change the look by switching to some other icon library.
Now let’s import the icons into our table component where we want to fix the issue.
import tableIcons from "./MaterialTableIcons";
Next, pass the icons into the table. Update your code with the additional icons prop:
<MaterialTable title="Table" icons={tableIcons} columns={columns} data={data} />;
Now our table will look something like this:
Now it’s perfect! Let’s explore some other features.
We can add two types of actions to our material-table:
We can easily add actions by passing an actions
array prop into the table.
If the action is row-specific, we don’t need to say anything. If the action is for the whole table, we have to pass isFreeAAction = true
into the table.
Here’s an example:
import MaterialTable from "material-table"; import tableIcons from "./MaterialTableIcons"; export const TableWithActions = () => { return ( <MaterialTable title="Table with actions" actions={[ { icon: tableIcons.Delete, tooltip: "Delete User", onClick: (event, rowData) => alert("You want to delete " + rowData.name), }, { icon: tableIcons.Add, tooltip: "Add User", isFreeAction: true, onClick: (event) => alert("You want to add a new row"), }, ]} ... other props as before /> ); };
We are removing the data
and column
props to remove duplicates. Our table will have two additional buttons now — one in the top right corner (Add button) and a Delete button for each row.
This enables you to specify the actions for your table. There is a disabled
property through which you can determine whether you want to enable action or not.
Now let’s say you don’t like the look of the action buttons and you want to render a custom action button. Or maybe you don’t like how the table cells look. material-table enables you to change the look and feel of any component.
To achieve this, the MaterialTable
component takes an additional parameter named components
where you can override almost any part of the table.
To show how this works, we’ll modify the Delete button from our previous example. Let’s say we don’t want a Delete icon; instead, we want an actual text button.
All we have to do is add the following prop to the table:
<MaterialTable components={{ Action: (props) => ( <button onClick={(event) => props.action.onClick(event, props.data)}> Custom Delete Button </button> ), }} ... other props />
Now our table will look like this:
See the material-table docs for a complete list of customization options.
It’s nice that we can override the components that are provided by material-table. But what about rendering our own columns? We’ve seen that we pass the columns and data props into the table and material-table itself takes care of the rendering.
Now let’s say we have an image URL that comes from a remote source. We don’t want to render the URL (because that’s stupid 😛).
In this scenario, we can render whatever we like, such as an image component, for example.
To do that, we have to modify the columns that we pass into the table.
Let’s modify our previous example like the following to add imageUrl
and render that:
import MaterialTable from "material-table"; import tableIcons from "./MaterialTableIcons"; const data = [ { name: "Mohammad", surname: "Faisal", birthYear: 1995, imageUrl: "https://avatars0.githubusercontent.com/u/7895451?s=460&v=4", }, { name: "Nayeem Raihan ", surname: "Shuvo", birthYear: 1994, imageUrl: "https://avatars0.githubusercontent.com/u/7895451?s=460&v=4", }, ]; const columns = [ { title: "Avatar", field: "imageUrl", render: (rowData) => <img src={rowData.imageUrl} style={{ width: 40, borderRadius: "50%" }} />, }, { title: "Name", field: "name" }, { title: "Surname", field: "surname" }, { title: "Birth Year", field: "birthYear", type: "numeric" }, ]; export const ImageTable = () => { return <MaterialTable title="Basic Table" icons={tableIcons} columns={columns} data={data} />; };
Now our table will look like this:
.csv
and .pdf
The export feature is by far my favorite feature of the material-table library. When you’re building data-heavy applications, you may need to enable users to export the data to Excel or PDF. Traditionally you would have to use some custom Excel library to do that job.
With material-table, you simply pass an option that says exportButton : true
, and you’re good to go.
import MaterialTable from "material-table"; export const BasicTable = () => { return ( <MaterialTable ... other props options={{ exportButton: true, }} /> ); };
Now there will be an additional icon at the top of the table that enables users to download data both in .csv
and .pdf
format.
Just a single line of code, and you’ve already supercharged your table.
Let’s say you have a list of people, each of whom has a name
, age
etc. If you want to see people of the same age, how do you show this in your table?
material-table enables you to group data super easily. In any other type of table, this would be an extremely difficult thing to do dynamically.
With material-table, all you need to do is pass another option named grouping :true
.
<MaterialTable ... other props options={{ grouping: true, }} />
Now you should see something like the following if you drag the Name
column header to the top of the table:
material-table internally uses another awesome library named React DnD to achieve this.
We have already seen that, by default, material-table enables us to search through data. You can override the look and feel of the search bar by adding styles in the options.searchFieldStyle
.
If you don’t want to show the search bar for some reason, you will have to pass search : false
, like so:
<MaterialTable // other props options={{ search: false }} />
Now there should be no search field in your table:
material-table also enables you to sort your data very easily. All you have to do is pass another option, sorting : true
.
<MaterialTable ... other props options={{ sorting: true }} />
All your columns should now be sortable:
Remember, by default, material-table will try to sort your data lexicographically. If you want to sort with some other mechanism, you can modify that by overriding the columns property, like so:
columns={[ { title: 'Name', field: 'name', customSort: (a, b) => a.name.length - b.name.length }, .. other columns ]}
This give you complete control over how you sort your data.
We focused on material-table for this tutorial, but there are certainly other table components to use in your React app. Let’s highlight a few prominent alternatives to material-table.
rsuite-table has a wide range of features and is great for creating professional-looking tables. It supports almost all the features we we mentioned above but doesn’t have a download data option.
Also, data grouping is not supported out of the box.
React Table is not a component library like the others mentioned here; it is a collection of hooks that enables you to add features to any table component. According to the official website, React Table is a “table utility, not a table component.”
You can use React Table to add sorting, filtering, grouping, pagination, etc., to any table component.
MUI-Datatables is a lightweight version of material-table. If you don’t need the full range of features described above, this library offers a lighter alternative.
As robust and feature-rich as material-table is, maintenance and support have waned since the original creator left the project. Other contributors are still maintaining the library, though not as actively.
However, there is a fork of the current project called material-table/core. This version is not yet as polished as material-table, but it is under active development and frequently updated.
Although it doesn’t yet support the entire range of features described above, material-table/core is compatible with the new Material-UI v5, which is great!
material-table remains the more popular library, with roughly 150,000 downloads per week as opposed to material-table/core’s 23,000 weekly downloads at the time of writing. But material-table/core is a viable alternative if you’re using the latest version of Material-UI and looking for a library with more active support.
As you can see, material-table solves almost all the problems and covers all the use cases you’re likely to encounter when building data-heavy React apps. It’s almost too good to be true.
But there is a catch: material-table is heavier than all the alternatives mentioned above. Using bundlephobia, we can see that material-table
is over 200KB when used in production.
So if your data needs are simple, using this library might be overkill. You may want to choose some other lightweight library, such as rsuite-table, which is around 30KB in size, or even Rect Table, which is only 15KB.
I hope this guide helps you make a more informed decision next time you need to choose a table library for your React project.
The complete code used in this demo is available on GitHub.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowconsole.time is not a function
errorExplore the two variants of the `console.time is not a function` error, their possible causes, and how to debug.
jQuery 4 proves that jQuery’s time is over for web developers. Here are some ways to avoid jQuery and decrease your web bundle size.
See how to implement a single and multilevel dropdown menu in your React project to make your nav bars more dynamic and user-friendly.
NAPI-RS is a great module-building tool for image resizing, cryptography, and more. Learn how to use it with Rust and Node.js.
3 Replies to "Using material-table in React to build feature-rich data tables"
Hi great article.
One thing though. Since the original owner abandoned the library, I would recommend used the actively supported community fork material-table-core. With many bug fixes, we also provide support for Mui v5 via the @next tag.
Thanks for the note! We’ve addressed this and updated the post accordingly.
I really love how this article covers everything and its super clear to understand.
I believe there is one small section missing though.
In the custom rendering section where the image is added to the table, shows the change to the data including the image URL, but there is no code example showing how to create a custom row and pass an avatar component to it. Or did I miss something?