Tanstack Table: How to Add Tables to your React Application

·

11 min read

Originally published at blog.openreplay.com

Attention all frontend enthusiasts and curious minds! Are you tired of struggling with creating React tables that look good and perform even better? Well, have no fear because Tanstack Table is here! Its headless design doesn't ship with components and markup styles, giving us full control over markup and styles.

A Tanstack table is a workhorse, it's built to filter, sort, group, aggregate, paginate, materialize, and display massive datasets. It offers many improved features such as performance, support for server-side rendering (SSR), new hooks, and a more modern, flexible API. In this article, we're going to focus on how to get started with adding and styling our ReactJS tables using Tanstack Table. So, let's dive in and see what Tanstack Table has to offer!

Prerequisites

The following requirements should be satisfied.

  1. Node.js: In our project, we'll use the NPM package manager for the installation of Tanstack/React-table.

  2. React: Tanstack/React-Table is a React library, so you should have a basic understanding of React before you start using it.

Once we have these prerequisites installed, we can install React-Table using NPM or Yarn, but first let's have a preview of the Table API.

Preview of The Table API

The Table API is a collection of functions and objects that are used to build and render tables, below are the library adapters to choose from.

Tanstack Javascript Adapters

  1. React Table

  2. Solid Table

  3. Svelte Table

  4. Vue Table

  5. Vanilla JS/TS

Here are some of the key features of the Tanstack React Table API:

  1. columns: This is an array of objects that define the columns of the table. Each object should contain at least a Header property, which is the label for the column, and an accessor property, which is a function that returns the value for a column based on the data.

  2. data: The data property is an array of objects that represent the rows of the table. Each object should have properties that correspond to the accessor functions defined in the columns array.

  3. useReactTable: This is a hook that creates a new instance of a table and returns the necessary functions and objects to render it. The functions include getTableProps, getTableBodyProps, headerGroups, and rows.

  4. getTableProps: The getTableProps function is a function that returns a set of props that should be applied to the <table> element.

  5. getTableBodyProps: The getTableBodyProps function is a function that returns a set of props that should be applied to the <tbody> element.

  6. headerGroups: The headerGroups property is an array of objects that represent the header groups of the table. Each object contains a headers property, which is an array of the columns in that group.

  7. rows: The rows property is an array of objects that represent the rows of the table. Each object contains a cells property, which is an array of objects that represent the cells in that row.

  8. prepareRow: This function prepares a row object for rendering by applying any necessary styles or props.

Installing and Importing Tanstack Table

Before we dig into the API, it's important to note that we will be using Typescript with React, so when initializing your project choose Typescript as the preferred language, this can easily be done using NextJS or ViteJS.

Let's get set up! With our terminal pointing to our project folder, we can run the following command to install the Tanstack adapter for ReactJS.

npm install @tanstack/react-table

Next, we have to import it in order to use it. Quick, in our project working directory, navigate to our source folder, create a new folder, and name it components. In that folder, create a file, name it tanstack.tsx, and then add the following code.

import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";

The code imports two of the most important functions from the library: createColumnHelper and useReactTable.

-. createColumnHelper is a helper function used to create column definitions for our table. -. useReactTable is the main hook function that we'll use to generate a table instance based on our data and column definitions.

Here's the structure for our working directory.

Preparing the Data

We will save our data in JSON format, so if you don't already have it, we have a copy available. To use it in the application, navigate to the components folder, create a new file named data.json, and paste the data as an array of objects.

We need to import the file into our React component tanstack.tsx.

tanstack.tsx

import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from '@tanstack/react-table'

import { defaultData } from './data.json';

Now that we have everything we need, we can define our table schema and create the table.

Setting Up Tables with Tanstack Table

In this section, we are going to use most of the functions available in the Table API. Let's jump in and start defining our table. The first step is to create an object with the properties that we need from our data. In TypeScript, we can do this by defining a TypeScript type object. In our case, let's call it Student. This will later indicate that it holds an array defined as Student.

tanstack.tsx

type Student = {
  Application_No: number;
  Name: string;
  Father_Name: string;
  DOB: string;
};

We defined a TypeScript type Student that represents an object with properties such as Application No, Name, Father Name, and DOB.

Next, we'll create a new instance of the createColumnHelper function, which we'll use to define the table columns.

tanstack.tsx

const columnHelper = createColumnHelper<Student>();

We will use the createColumnHelper() function to create a columnHelper object that we'll use to define a columns object.

To complete our table schema, we need to create the columns object. It should contain an array of objects, with each object representing a column in the table. We create each object using the columnHelper.accessor() function, which returns an object with configuration options for the corresponding column.

tanstack.tsx

const columns = [
  columnHelper.accessor("Application_No", {
    header: "Registration No",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Name", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Father_Name", {
    header: "Father Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("DOB", {
    header: "Date of Birth",
    cell: (info) => info.getValue(),
  }),
];

The accessor() function of the columnHelper specifies the data accessor for each column, which extracts the value of a property from the row of data in the data.json file.

We have successfully defined our table columns, and our code should be like the one below.

tanstack.tsx

import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";

import defaultData from "./data.json";

type Student = {
  Application_No: number;
  Name: string;
  Father_Name: string;
  DOB: string;
};

const columnHelper = createColumnHelper<Student>();

const columns = [
  columnHelper.accessor("Application_No", {
    header: "Registration No",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Name", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Father_Name", {
    header: "Father Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("DOB", {
    header: "Date of Birth",
    cell: (info) => info.getValue(),
  }),
];

The next section will determine the final table output. In our tanstack.tsx file, below thecolumns object, we will define a React functional component named Table. This function will render a table using the useReactTable hook that we had imported earlier.

tanstack.tsx

function Table() {
  const [data, setData] = useState<Student[]>(defaultData);

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

  return <></>;
}

The Table function starts by initializing the data state to an array of Student objects using useState hook, where each Student object will represent a row of the table.

Next, our useReactTable hook is called to create a table object. The hook takes an object with three properties:

  • data: An array of data that represents the rows of the table.

  • columns: An array of objects that represent the columns of the table.

  • getCoreRowModel: A function that returns a row model that specifies how the rows should be displayed.

The table object will render our table in the return statement of the Table function.

Before rendering our table, it's key that we know what the <th>, <td>, and <tr> elements represent in tables.

<th>: This element is used to define a header cell in a table. A header cell is typically used to label the columns or rows of the table and is displayed in bold to differentiate it from the other cells.

<td>: This element is used to define a standard cell in a table. Standard cells contain the main content of the table and are usually displayed with normal text formatting.

<tr>: This element is used to define a row in a table. A row is made up of one or more cells, and each cell is defined using either a <th> or <td> element.

By combining these elements, we can create tables that display information in a structured and organized way.

Let's return to our Table component and add the following code to our return statement.

tanstack.tsx

return (
  <div>
    <table>
      <thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th key={header.id}>
                {header.isPlaceholder
                  ? null
                  : 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>
  </div>
);

In the return section, we render the table using the data and columns defined earlier. We start by creating a <div> element that contains a <table> element. Inside the <table>, there is an <thead> element that contains rows with table headers.

For each header group returned by the table.getHeaderGroups(), we used the map method to iterate over the headers and created a <th> element for each one. If the header in place is a placeholder, then null is returned, and if not, the flexRender function is used to render the header element.

The next section is the <tbody> element, which contains rows with table cells. For each row in the table, we used the map method to iterate over the cells and created a <td> element for each one. Inside each <td>, the flexRender function is used to render the cell element.

Let's not forget to import our Table component to our root component.

import Table from "@/components/tanstack";

export default function Home() {
  return (
    <>
      <Table />
    </>
  );
}

Let's run our project and check the rendered table in our browser.

Styling the Tanstack Table

Our table is now functional, but lacks visual appeal. To style it, we'll use Styled-Components, which offers a CSS-in-JS approach that enables us to write structured and readable CSS code

If you have not been using Styled Components, we recommend checking their documentation. We will only need two styled-components, TableContainer and StyledTable. TableContainer defines a div element with CSS styles applied as our table container.

Since Styled Components use regular CSS syntax to define styles, our StyledTable component will use th and td CSS selectors to target the <th> and <td> elements, respectively.

In the styles folder, Let's create a new tableStyles.ts file, here we will create all our required table styles.

/styles/tableStyle.ts

import styled from "styled-components";

export const TableContainer = styled.div`
  position: relative;
  left: 10%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  max-width: 800px;
`;

export const StyledTable = styled.table`
  width: 100%;
  border-collapse: collapse;
  margin-top: 20px;
  font-size: 0.9em;

  th {
    background-color: slategray;
    color: white;
    padding: 5px;
    border: 1px solid #ddd;
    text-align: left;
  }

  td {
    background-color: hsl(240, 50%, 90%);
    padding: 5px;
    border: 1px solid #ddd;
    text-align: left;
  }

  tr:nth-child(even) td {
    background-color: hsl(240, 50%, 85%);
  }

  tr:hover td {
    background-color: #ddd;
  }
`;

The tr:nth-child(even) td selector applies a background color to every row of <td> elements, creating a striped effect. The tr:hover td selector applies a background color to the <td> element when a user hovers over a row.

Next, we need to update our Table component with these styled-components. We are going to import and replace them with the respective tsx elements.

import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";

import defaultData from "./data.json";
import { useState } from "react";
import { StyledTable, TableContainer } from "@/styles/tableStyle";

type Student = {
  Application_No: number;
  Name: string;
  Father_Name: string;
  DOB: string;
};

const columnHelper = createColumnHelper<Student>();

const columns = [
  columnHelper.accessor("Application_No", {
    header: "Registration No",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Name", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("Father_Name", {
    header: "Father Name",
    cell: (info) => info.getValue(),
  }),
  columnHelper.accessor("DOB", {
    header: "Date of Birth",
    cell: (info) => info.getValue(),
  }),
];

function Table() {
  const [data, setData] = useState<Student[]>(defaultData);

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

  return (
    <TableContainer>
      <StyledTable>
        <thead>
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <th key={header.id}>
                  {header.isPlaceholder
                    ? null
                    : 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>
      </StyledTable>
    </TableContainer>
  );
}
export default Table;

Congratulations! For making it this far, our final Table version is now styled.

Now that the table looks great, you may want to check Tanstack's documentation for other features such as adding pagination and sorting to improve the table's usability further. They can help users navigate through large data sets and make it easier to find the information they need.

Conclusion

The process presented here exemplifies a well-structured Tanstack React table. By using type annotations and following best practices for React development, the code ensures that the table is easily maintainable and extensible in the future.

It is essential to note that this is just one way to create a table in React. We may use alternative approaches depending on the application's specific requirements. By adopting the principles showcased, we can craft clean, maintainable code that effectively fulfills our needs.

References

  1. Tanstack documentation

  2. TanStack Table v8