The TanStack suite provides the @tanstack/react-table library (formerly known as React Table), a complete solution for displaying data in grid or tabular format.
There has been a big change if you are migrating from TanStack Table version 7 to 8. The developers completely changed the API, and now it is entirely built on TypeScript and has strong type support.
It provides headless UI, meaning it does not force you to any specific UI; you can create however you want and have complete control over it.
Project Overview
In this small project, we will initialize a project in React using the Vite build tool and then install MUI to style our application.
Then, we will use the JSONPlaceholder API as a data source and use the axios.get() method to fetch the data from it.
In the next step, we will create a Table component using the TanStack Table to display the data correctly.
Here is the step-by-step guide:
Step 1: Initialize a React Project
Let’s create a new React app using Vite:
npm create vite@latest tanstack-table-app -- --template react
Navigate to the project directory and install the packages using the command below:
cd tanstack-table-app npm install
Now, install the MUI, Axios, and TanStack table packages using the command below:
npm install axios @tanstack/react-table @mui/material @emotion/react @emotion/styled @popperjs/core
Keep in mind that we are using @tanstack/react-table version 8.19.3.
Step 2: Creating an MUI Theme
Under the “src” folder, create a new folder called “styles”.
Under the “styles” folder, create a new file called “theme.js” and add the code below:
import { createTheme } from '@mui/material/styles'; const theme = createTheme({ palette: { primary: { main: '#1976d2', }, secondary: { main: '#dc004e', }, }, }); export default theme;
Step 3: Fetch Data from JSONPlaceholder API
As we already discussed, we will create a custom hook that will fetch the data from JSONPlaceholder API using axios.get() method.
Under the “src” folder, create a new folder called “hooks”, and inside that folder, create a new file called “useFetchData.js” and add the code below in it:
import { useState, useEffect } from 'react'; import axios from 'axios'; const useFetchData = (url) => { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const URL = "https://jsonplaceholder.typicode.com/users"; useEffect(() => { const fetchData = async () => { try { const response = await axios.get(URL); setData(response.data); } catch (err) { setError(err); } finally { setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; }; export default useFetchData;
Basically, what we are doing here is abstracting the logic for fetching data from a URL. This hook function returns three things for a component to manage:
- data: An actual data fetched from an API.
- loading: It is a loading state that will be displayed while React tries to fetch the data.
- error: It is an error state that is visible when you encounter any error.
Step 4: Define the columns separately
To make our code more modular, we will define the columns separately.
Under the “src” folder, create a new folder called “data”, and inside that folder, create a new file called “tableColumns.js” and add the below code in it:
const userColumns = [ { header: 'ID', accessorKey: 'id', }, { header: 'Name', accessorKey: 'name', }, { header: 'Username', accessorKey: 'username', }, { header: 'Email', accessorKey: 'email', }, ]; export default userColumns;
With this header and accessorKey, we should not need to manually add ID properties, as accessorKey will be used as the ID by default.
Step 5: Create a TableComponent
The @tanstack/react-table module provides the following hook and functions:
- useReactTable: It is a library hook to create a table instance with configuration.
- getCoreRowModel: It is a function that provides the core row model for the table.
- flexRender: It is a function that renders cells and headers flexibly.
With these options, you can clearly display the data in a proper tabular format.
Under the “src” folder, create a new folder called “components”.
Inside the “components” folder, create a new file called “TableComponent.jsx” and add the code below:
import { useReactTable, getCoreRowModel, flexRender, } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableContainer as MuiTableContainer, TableHead, TableRow, Paper, } from "@mui/material"; // eslint-disable-next-line react/prop-types const TableComponent = ({ columns, data }) => { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), }); return ( <MuiTableContainer component={Paper}> <Table> <TableHead> {table.getHeaderGroups().map((headerGroup) => ( <TableRow key={headerGroup.id}> {headerGroup.headers.map((header) => ( <TableCell key={header.id}> {flexRender( header.column.columnDef.header, header.getContext() )} </TableCell> ))} </TableRow> ))} </TableHead> <TableBody> {table.getRowModel().rows.map((row) => ( <TableRow key={row.id}> {row.getVisibleCells().map((cell) => ( <TableCell key={cell.id}> {flexRender(cell.column.columnDef.cell, cell.getContext())} </TableCell> ))} </TableRow> ))} </TableBody> </Table> </MuiTableContainer> ); }; export default TableComponent;
Basically, we defined a TableComponent, which is a child component that accepts the following two props:
- columns: These are the columns that we already defined in the tableColumns.js file.
- data: This is the fetched data from an API.
The sole responsibility of this TableComponent is to handle the rendering of both the table head and body, iterating over header groups, headers, rows, and cells to display the data dynamically.
Step 6: Create a TableContainer component
We created a TableComponent, but we need to create a container that contains the Table.
Under the “components” folder, create a new file called “TableContainer.jsx” and add the below code in it:
import { CircularProgress, Typography } from "@mui/material"; import TableComponent from "./TableComponent"; // eslint-disable-next-line react/prop-types const TableContainer = ({ columns, data, loading, error }) => { if (loading) { return <CircularProgress />; } if (error) { return ( <Typography color="error"> An error occurred: { // eslint-disable-next-line react/prop-types error.message} </Typography> ); } return <TableComponent columns={columns} data={data} />; }; export default TableContainer;
Step 7: Update the App.jsx component
Update the primary application component src/App.jsx to use all modular components:
import { ThemeProvider } from "@mui/material/styles"; import theme from "./styles/theme"; import TableContainer from "./components/TableContainer"; import useFetchData from "./hooks/useFetchData"; import userColumns from "./data/tableColumns"; const App = () => { const { data, loading, error } = useFetchData(); return ( <ThemeProvider theme={theme}> <div style={{ padding: "20px" }}> <TableContainer columns={userColumns} data={data} loading={loading} error={error} /> </div> </ThemeProvider> ); }; export default App;
This is the main parent component through which we are passing all the data as props to subsequent child components.
Here, in the TableContainer component, we passed columns, data, loading, and error as props to the child component. The child component then accepts the props and displays the data in a tabular format defined by the configuration.
Also, update the src/main.jsx file:
import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.jsx"; ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <App /> </React.StrictMode> );
Here is the complete project structure of our small web application:
tanstack-table-app/ ├── src/ │ ├── components/ │ │ ├── TableComponent.jsx │ │ └── TableContainer.jsx │ ├── hooks/ │ │ └── useFetchData.js │ ├── data/ │ │ └── tableColumns.js │ ├── styles/ │ │ └── theme.js │ ├── App.jsx │ └── main.jsx
Save the file and start the Vite development server using this command below:
npm run dev
Go to this URL: http://localhost:5173/
Here is the complete code on GitHub.
That’s all!