React – Material-Table Library with Axios
Table of Contents
Overview
In this example, we will build a small react app with Material UI, Material-Table, and Axios. The Material-Table library is great option for a feature rich data grid that handles pagination, sorting, filtering, in-line edits, and grouping. The library is well documented like Material UI and has over 100k downloads a week. Using Axios, the app will call randomuser.me to fetch 20 users. You can download the source files from https://github.com/fullstacksoup/blog-react-material-grid-demo.

Prerequisites
First create the React Application.
npx create-react-app your-app-name
Install the following libraries with NPM.
npm install @material-ui/core
npm install @material-ui/icons
npm install material-table
npm install axios
Building the Component
Create a folder labeled Components. Under the Components folder create a component labeled MatDataTable.js and another one labeled DetailTable.js.
The following are pieces for MatDataTable.js
Imports
import React, { Component } from "react";
import axios from 'axios'; // npm instal axios
import { forwardRef } from 'react';
import MaterialTable from "material-table";
import EditIcon from '@material-ui/icons/Edit';
import DeleteIcon from '@material-ui/icons/Delete';
import SaveIcon from '@material-ui/icons/Save';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Search from '@material-ui/icons/Search';
// Import the detail table if you want use the collapsible section
import DetailTable from './DetailTable';
Table Icons

Create constants for the table icons. These are the icons used throughout Material-Table and must be defined or it will render the initials of the icon.
MatDataTable.js icon definitions.
const tableIcons = {
Edit: forwardRef((props, ref) => <EditIcon {...props} ref={ref} />),
Delete: forwardRef((props, ref) => <DeleteIcon {...props} ref={ref} />),
Check: forwardRef((props, ref) => <SaveIcon {...props} ref={ref} />),
Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
DetailPanel: forwardRef((props, ref) => <ChevronRight {...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} />),
};
Component Properties
Create a class component labeled MatDataTable.js, setup the property person[] to hold the random users data from randomuser.me.
export default class MatDataTable extends Component {
constructor(props) {
super(props);
this.state = {person: []};
}
Fetching Data – API Call
Taking advantage of the class component componentDidMount() event, call randomuser.me and fetch 20 users. Added a couple of console.log() function to see if there is data.
componentDidMount(prevProps) {
const maxResults = 20;
const url = `https://randomuser.me/api/?results=${maxResults}`;
axios.get(url)
.then(results => {
console.log(results);
console.log(results.data.results);
this.setState({ person: results.data.results });
var newArr = results.data.results.map(function(val) {
return {
id: val.id.value,
gender: val.gender,
login: val.login.username,
email: val.email,
cell: val.cell,
image: val.picture.thumbnail,
name: val.name.first + ' ' + val.name.last,
};
});
console.log(results.data.results);
this.setState({
tableArray: newArr //set state of the weather5days
},()=> {
console.log(this.state.tableArray);
console.log('this.tableArray ',this.state.tableArray);
});
});
}
Rendering the Table
Finally, render the table with the grouping options set to true. The grouping option allow you to drag and drop a column header as shown in the partial code below.
<MaterialTable
icons={tableIcons}
options={{
grouping: true
}}
detailPanel={rowData => {
return (
<DetailTable />
)
}}
.....

The render() section of the component.
render() {
return (
<div style={{ maxWidth: "50%", marginLeft: "300px", marginTop: "100px" }}>
<MaterialTable
icons={tableIcons}
options={{
grouping: true
}}
detailPanel={rowData => {
return (
<DetailTable />
)
}}
columns={[
{
title: 'Image',
field: 'image',
render: rowData => (
<img
style={{ height: 36, borderRadius: '50%' }}
src={rowData.image}
/>
),
},
{ title: "Name", field: "name", type: "numeric", align: 'left' },
{ title: "Gender", field: "gender"},
{ title: "Email", field: "email" },
{ title: "Cell Phone", field: "cell", type: "numeric" }
]}
data={this.state.tableArray}
title="Demo Title"
/>
</div>
);
}
}
All together – Complete Component:
import React, { Component } from "react";
import axios from 'axios'; // npm instal axios
import { forwardRef } from 'react';
import MaterialTable from "material-table";
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Search from '@material-ui/icons/Search';
import DetailTable from './DetailTable';
const tableIcons = {
DetailPanel: forwardRef((props, ref) => <ChevronRight {...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} />),
};
export default class MatDataTable extends Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount(prevProps) {
const maxResults = 20;
const url = `https://randomuser.me/api/?results=${maxResults}`;
axios.get(url)
.then(results => {
console.log(results);
console.log(results.data.results);
this.setState({ person: results.data.results });
var newArr = results.data.results.map(function(val) {
return {
id: val.id.value,
gender: val.gender,
login: val.login.username,
email: val.email,
cell: val.cell,
image: val.picture.thumbnail,
name: val.name.first + ' ' + val.name.last,
};
});
console.log(results.data.results);
this.setState({
tableArray: newArr //set state of the weather5days
},()=> {
console.log(this.state.tableArray);
console.log('this.tableArray ',this.state.tableArray);
});
});
}
render() {
return (
<div style={{ maxWidth: "50%", marginLeft: "300px", marginTop: "100px" }}>
<MaterialTable
icons={tableIcons}
options={{
grouping: true
}}
detailPanel={rowData => {
return (
<DetailTable />
)
}}
columns={[
{
title: 'Image',
field: 'image',
render: rowData => (
<img
style={{ height: 36, borderRadius: '50%' }}
src={rowData.image}
/>
),
},
{ title: "Name", field: "name", type: "numeric", align: 'left' },
{ title: "Gender", field: "gender"},
{ title: "Email", field: "email" },
{ title: "Cell Phone", field: "cell", type: "numeric" }
]}
data={this.state.tableArray}
title="Demo Title"
/>
</div>
);
}
}
Expandable Sections
Add the detail table in the expand and collapse section.

Create another component labeled DetailTable.js and add the following code.
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
const useStyles = makeStyles({
table: {
minWidth: 650,
},
});
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function DetailTable() {
const classes = useStyles();
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
Routing (Optional)
If you already have routing and navigation in your app ignore the rest of this post.
If you just want to see the table without routing, then you can do the following.
In the App.js component update the code to the following,
import React, { Component } from 'react';
import './App.css';
import MatDataTable from './Components/MatDataTable';
class App extends Component {
render() {
const spacing = 5;
return (
<div className="App" >
<MatDataTable />
</div>
);
}
}
export default App;
TIP – Render HTML Value
I do not recommend the following If your JSON data has HTML then you can do the following to render it properly.
Using dangerouslySetInnerHTML
columns={[
{ title: "Title", field: "Title", align: "left",
render: rowData => (
<div dangerouslySetInnerHTML={{ __html: rowData.Title }} />
),
},
{ title: "Company", field: "Company"},
{ title: "PostDate", field: "PostDate", type: "date" },
...
Since this can be a security risk, I will try to come up with a better solution and post it here.
You must be logged in to post a comment.