How to Integrate the Material UI Data Grid in React Using Data from a REST API

When building applications that need to display complex tabular data, it can be tempting to try to create your own custom data grid component from scratch using HTML tables or CSS grid. However, there are many non-trivial challenges you‘ll quickly run into, such as implementing pagination, sorting, filtering, virtualized scrolling, Excel-like navigation, accessibility, and more.

Thankfully, there‘s no need to reinvent the wheel. Material UI offers a powerful, full-featured Data Grid component that provides all that functionality out of the box, with an easy-to-use but highly customizable API. In this post, we‘ll learn how to integrate the Data Grid into a React app and populate it with live data from a REST API.

Benefits of using the Material UI Data Grid

Before we dive into implementation details, let‘s consider some of the key benefits of leveraging the Material UI Data Grid component in your React apps:

  • Feature-rich: The Data Grid comes with essential features like pagination, sorting, filtering, row selection, column resizing/reordering/pinning, grouping, CSV export, and more. It would take significant development effort to implement this functionality yourself.

  • Customizable: While the grid has a polished look and feel out of the box, almost every aspect of its appearance and behavior can be tweaked to fit your specific design and UX needs. You can customize columns, rows, cells, headers and footers via the component API and CSS.

  • Accessible: The grid is designed with accessibility in mind, supporting features like keyboard navigation, ARIA attributes, and focus management. This makes it usable for people with disabilities and ensures your app is compliant with web accessibility standards.

  • Performant: The Data Grid is optimized to handle large datasets up to 100,000 rows with smooth scrolling and fast updates. It leverages virtualization under the hood to only render visible rows, and offers further performance features like row aggregation and lazy loading.

  • Extensible: The grid provides a plugin system that allows you to add custom functionality via grid and column extensions. This makes it easy to implement advanced capabilities like cell editing, custom widgets, row grouping, detail panels, and more.

Installing and importing the Data Grid

To start using the Data Grid, you‘ll first need to install the @mui/x-data-grid package via npm or yarn:

npm install @mui/x-data-grid

Note that the grid has a peer dependency on @mui/material, so make sure that package is also installed.

Once installed, you can import the DataGrid component in your React component files:

import { DataGrid } from ‘@mui/x-data-grid‘;

Defining the grid‘s columns and rows

The next step is to define the schema of your data – what columns will appear in the grid, and what data will be displayed in each row.

Columns are configured via the columns prop, which takes an array of objects with the following properties:

  • field (string, required): The key of the row object that this column should display
  • headerName (string, optional): The text to display in the column header. Defaults to field.
  • width (number, optional): The width of the column in pixels. Defaults to 100.
  • type (string, optional): The data type of the field, which determines default formatting, sorting, and filtering behavior. Can be ‘string‘, ‘number‘, ‘date‘, ‘dateTime‘, or ‘boolean‘.

Here‘s an example columns configuration:

const columns = [
  { field: ‘id‘, headerName: ‘ID‘, width: 70 },
  { field: ‘firstName‘, headerName: ‘First name‘, width: 120 },
  { field: ‘lastName‘, headerName: ‘Last name‘, width: 120 },
  { field: ‘age‘, headerName: ‘Age‘, type: ‘number‘, width: 90 },
];

Rows are configured via the rows prop, which should be an array of objects whose keys match the field properties defined in columns:

const rows = [
  { id: 1, lastName: ‘Snow‘, firstName: ‘Jon‘, age: 35 },
  { id: 2, lastName: ‘Lannister‘, firstName: ‘Cersei‘, age: 42 },
  // ...
];

In a real app, you‘ll likely be fetching this rows data from an external API or database. Let‘s look at how to do that next.

Fetching data from a REST API

To display data from a REST API in the grid, we need to make an HTTP request to the API endpoint and then transform the response data into the rows format expected by the grid.

Here‘s an example using the Fetch API to load user data from the /users endpoint of the JSON Placeholder API:

import { useState, useEffect } from ‘react‘;

function UserGrid() {
  const [rows, setRows] = useState([]);

  useEffect(() => {
    fetch(‘https://jsonplaceholder.typicode.com/users‘)
      .then(response => response.json()) 
      .then(data => setRows(data));
  }, []);

  // Render Data Grid with rows
}

A few things to note here:

  • We initialize the rows state as an empty array
  • Inside a useEffect hook, we make a GET request to the /users endpoint using the Fetch API
  • When the response comes back, we parse the JSON data and update the rows state, which triggers a re-render of the grid with the new data

Alternatively, if you‘re using a library like Axios for data fetching, the code would look like:

import axios from ‘axios‘;

function UserGrid() {
  const [rows, setRows] = useState([]);

  useEffect(() => {
    axios.get(‘https://jsonplaceholder.typicode.com/users‘)
      .then(response => setRows(response.data)); 
  }, []);

  // Render Data Grid with rows
}  

Axios simplifies the code a bit by eliminating the need to parse the JSON manually.

Rendering the Data Grid

Once you have your columns and rows defined, you can render the Data Grid in your component‘s JSX:

import { DataGrid } from ‘@mui/x-data-grid‘;

function UserGrid({ rows, columns }) {
  return (
    <div style={{ height: 400, width: ‘100%‘ }}>
      <DataGrid rows={rows} columns={columns} />
    </div>  
  );
}

The grid requires a parent element with a fixed height so it knows how much vertical space it has to work with. You can set the width to ‘100%‘ to fill its container.

Here‘s a complete example combining the API data fetching logic with rendering the grid:

import { useState, useEffect } from ‘react‘;  
import { DataGrid } from ‘@mui/x-data-grid‘;

const columns = [
  { field: ‘id‘, headerName: ‘ID‘, width: 70 },
  { field: ‘name‘, headerName: ‘Name‘, width: 200 },
  { field: ‘username‘, headerName: ‘Username‘, width: 150 },
  { field: ‘email‘, headerName: ‘Email‘, width: 250 },
];

function UserGrid() {
  const [rows, setRows] = useState([]);

  useEffect(() => {
    fetch(‘https://jsonplaceholder.typicode.com/users‘)
      .then(response => response.json())
      .then(data => setRows(data));
  }, []);

  return (
    <div style={{ height: 400, width: ‘100%‘ }}>  
      <DataGrid rows={rows} columns={columns} />
    </div>
  );  
}

export default UserGrid;

With this code, you now have a basic data grid displaying user information loaded from the REST API. The grid supports sorting by clicking column headers and pagination using the controls at the bottom.

Enabling sorting, filtering & pagination

By default, the Data Grid already supports client-side sorting, filtering and pagination with zero additional configuration.

Pagination controls appear automatically at the bottom of the grid, allowing users to navigate between pages of rows. You can control the default page size and available page size options using the pageSize and rowsPerPageOptions props:

<DataGrid 
  rows={rows}
  columns={columns}   
  pageSize={5}
  rowsPerPageOptions={[5, 10, 20]}
/>

Clicking a column header will sort the rows by that column. To enable sorting for a column, its definition must have sortable set to true (this is the default). You can control the initial sort direction with initialState:

<DataGrid
  rows={rows}
  columns={columns}
  initialState={{
    sorting: {
      sortModel: [{ field: ‘name‘, sort: ‘asc‘ }],
    },
  }} 
/>

The grid also provides a search box in the toolbar for filtering rows by text. Filtering is enabled by default for all columns. If you only want certain columns to be filterable, set filterable: true on those column definitions.

While the built-in filtering and sorting may be sufficient for simple use cases, it‘s important to note that it all happens on the client-side. This means all the data must be loaded up-front, which doesn‘t scale well to very large datasets. Let‘s look at how to implement server-side pagination and sorting via the API.

Implementing server-side pagination & sorting

To scale the Data Grid to very large datasets, a common approach is to fetch data in chunks from the server using pagination parameters like page, perPage, sortBy, sortDir, filterBy, etc.

For example, say your REST API supports the following query parameters for the /users endpoint:

  • page: The current page number (default 1)
  • per_page: The number of results per page (default 10)
  • sort_by: The field to sort by
  • sort_dir: The sort direction (‘asc‘ or ‘desc‘)

You could modify the grid to pass its current page and sorting state to the API like this:

function UserGrid({ apiUrl }) {
  const [rows, setRows] = useState([]);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(10);
  const [sortModel, setSortModel] = useState([]);

  useEffect(() => {
    const params = {
      page,
      per_page: pageSize,
      sort_by: sortModel[0]?.field,        
      sort_dir: sortModel[0]?.sort,
    };

    const searchParams = new URLSearchParams(params);

    fetch(`${apiUrl}?${searchParams}`)  
      .then(response => response.json())
      .then(data => setRows(data));
  }, [apiUrl, page, pageSize, sortModel]);

  return (
    <div style={{ height: 400, width: ‘100%‘ }}>
      <DataGrid
        rows={rows}
        columns={columns}
        sortingMode="server"
        pagination
        pageSize={pageSize}
        rowsPerPageOptions={[10, 20, 50]}
        rowCount={100} // Assumes API returns total count
        paginationMode="server"
        onPageChange={newPage => setPage(newPage)} 
        onPageSizeChange={newSize => setPageSize(newSize)}
        onSortModelChange={newModel => setSortModel(newModel)} 
      />
    </div>
  );
}

Key changes:

  • Storing the current page, page size and sort model in state
  • Passing the page/sort parameters to the API via a query string
  • Setting sortingMode="server" and paginationMode="server" on the grid
  • Handling page, page size and sort model changes via callbacks
  • Passing rowCount to indicate the total number of results (assumes API can provide this)

With this setup, the grid will request new pages of data from the server as the user changes the page, page size or sorting. This allows the grid to scale to huge datasets while still providing a smooth UX.

Customizing columns and cells

One of the most powerful aspects of the Data Grid is how customizable it is. Almost every part of the grid can be tweaked to fit your app‘s needs.

Common customizations include:

  • Column sizes: Set width or flex on column definitions to control column widths
  • Column alignment: Set align to control header/cell text alignment per column
  • Cell formatting: Render custom content in cells using the renderCell prop
  • Header formatting: Render custom content in headers using the renderHeader prop
  • Row height: Set the row height for the entire grid or individual rows
  • Row selection: Allow users to select rows and access selected IDs
  • Styling: Customize colors, fonts, spacing, etc. using CSS and the component API

For example, here‘s how you might customize a column to format a number value as currency and align the header and cell content to the right:

const columns = [
  {
    field: ‘price‘,
    headerName: ‘Price‘,
    type: ‘number‘,
    width: 150,
    align: ‘right‘,
    headerAlign: ‘right‘,
    renderCell: params => 
      `$${params.value.toFixed(2)}`,
  },
  // ...
];  

Consult the Material UI Data Grid docs for complete API documentation and examples of all the ways you can customize the grid.

Advanced use cases

In addition to the core functionality covered so far, the Data Grid has many advanced features to support more complex use cases, such as:

  • Row editing: Allow inline editing of cell values
  • Row actions: Display a context menu or buttons to trigger actions on rows
  • Row grouping: Group rows by one or more columns with aggregate values
  • Cell virtualization: Lazily render cell content for improved performance with large datasets
  • Master-detail views: Show additional details for a row in an expandable sub-row
  • Tree data: Display hierarchical data using a tree structure with expandable nodes
  • Column pinning: Allow users to pin important columns to the left or right edge

Implementing these advanced features is beyond the scope of this post, but the Material UI docs have detailed guides and examples for each of these use cases and more.

Conclusion

The Material UI Data Grid is an incredibly powerful component that can replace the need to build complex tables yourself. Its extensive feature set, flexible customization options, and strong performance make it a great choice for displaying large datasets in your React apps.

In this post, we covered how to:

  • Install and import the Data Grid component
  • Define the grid‘s columns and rows
  • Fetch data from a REST API to populate the grid
  • Enable sorting, filtering, and pagination
  • Implement server-side pagination and sorting for scalability
  • Customize the grid‘s appearance and behavior

We also touched on some of the more advanced use cases the grid can support, like editing, grouping, infinite scrolling, and more.

The Data Grid is a complex component, and this post only scratched the surface of what it can do. To learn more, be sure to check out these additional resources:

With its comprehensive feature set and excellent documentation, the Material UI Data Grid is a powerful tool to add to your React development toolkit. Give it a try in your next project that needs to display complex tabular data!

Similar Posts