DEV Community

Cover image for Building an Advanced & Powerful Tables in Frontend
shrey vijayvargiya
shrey vijayvargiya

Posted on

Building an Advanced & Powerful Tables in Frontend

Complete guide to Tanstack table module to create powerful and advanced tables in frontend

Under the Hood

Welcome people, well, I don’t want this story to be long, jumping to the point of why I need to build and work with tables in the front end.

Well for our website iHateReading admin page, we need a lot of tables to manage content, users, database and a lot more.

You can understand how tables are often used in CRM and admin panels most or all of the admin panels or CRM have tables.

So why not work with tables I prefer using the Tanstack table this time.

For data fetching I am using JSON placeholder API.

JSON placeholder API

React/Next.js Boilerplate

For getting started easily, I have a bunch of boilerplates to use.

This boilerplate has the required libraries and architechture being installed already such as the following

  1. Next.js for the frontend framework
  2. Vercel for deployment
  3. Firebase as Database
  4. Github as version control
  5. Tailwind CSS as a styling library
  6. Material UI and Mantine.dev as UI library
  7. Redux as the state management library

2023 full stack repository boilerplate

Moving ahead with the same repository, you can clone the repository using CLI or command line tools.

Once the repository is cloned, the yarn or npm command can install the dependencies.

yarn or npm install
Enter fullscreen mode Exit fullscreen mode

This command will install all the required dependencies.

Tanstack Table

Let’s begin with understanding the Tanstack table.

Tanstack query is a headless UI table, meaning it doesn’t provide UI elements or DOM elements to render tables instead it uses methods to provide rows and columns and render the UI tables using simple HTML table elements.

If it’s difficult to understand, read about Headless UI on Google or ask GPT to explain.

Headless UI is a quite common technique in the modern frontend domain because of the following reasons —

  • Avoid adding multiple DOM elements,
  • Support various customisation,
  • Easy to scale,
  • Cross-compatible among javascript ecosystems. Tailwind CSS to mantine.dev and Shadcn/UI use headless UI and styling libraries concept.

The libraries as mentioned earlier only provide utility methods, hooks, and functions to accomplish the desired output such as styling and rendering UI elements.

Table Instance

Table instances provide tonnes of Table API, read more about them here

Table instance by tanstack table provides 7 methods defined on the following link

https://tanstack.com/table/latest/docs/api/core/table#table-api

Core APIs

Core APIs are the collection of methods provided by tanstack in-built methods or APIs.

Read the following things to understand

Column Def API

Column definitions are plain objects with the following options:

  • id
  • accessorKey
  • accessorFn
  • header
  • footer
  • cells

Columns API

These are core options and API properties for all columns. More options and API properties are available for others.

read more here

HeaderGroup APIs
These are core options and API properties for all header groups. More options and API properties.

This provides 3 methods

  • id
  • depth — The depth of the header group, zero-indexed based.
  • headers — An array of Header objects that belong to this header group

Rows API

Rows API provide 10+ methods to work with rows in the table.

read more in detail on this link

All kinds of required methods are defined in the list making things easy for developers.

Cells APIs

These are core options and API properties for all cells.

  • id
  • getValue
  • renderValue
  • row
  • column
  • getContext

read about all in detail here

Features API

Moving ahead with cool feature APIs tanstack provides under the hood making things easy such as filtering, sorting, querying, pagination and so on.

Following are the features provided by the tanstack table under the hood

Examples

The Tanstack website for this headless table does have an example section.

React Table Examples

Check the link above, tonnes of table examples are added such as pagination, ordering, filtering, drag-drop table rows and so on.

Installing React Tanstack Table

Read more about table instance

A Tanstack table usually provides a method that takes 2 arguments to provide the data such as columns and rows for the table.

import { useReactTable } from '@tanstack/react-table';
Enter fullscreen mode Exit fullscreen mode

Since I am using the table in the React.js ecosystem I’ve to import the useReactTable method, similarly, dev using stack table in Vue and Svelte and JS ecosystem needs the following methods to import.

read detailed installation over here

//vanilla js
const table = createTable({ columns, data })

//react
const table = useReactTable({ columns, data })
//solid
const table = createSolidTable({ columns, data })
//svelte
const table = createSvelteTable({ columns, data })
//vue
const table = useVueTable({ columns, data })

Enter fullscreen mode Exit fullscreen mode

By its name, the useReactTable method will help to create the table instance.

Data Fetching

For data sampling, I've used JSON placeholder API that provides a user list via the API endpoint response.

Data is fetched using axios and the useQuery react hook provided by again tanstack library helps to cache the API response.

const { data: allUsers, isLoading } = useQuery("allUsers", async () => {
  const response = await axios.get(
   "https://jsonplaceholder.typicode.com/posts?_limit=10"
  );
  return response.data;
 });
Enter fullscreen mode Exit fullscreen mode

If you are new to react-query hooks let me explain in the short

const { data: allUsers, isLoading } = useQuery("allUsers", async () => {
  const response = await axios.get(
   "https://jsonplaceholder.typicode.com/posts?_limit=10"
  );
  return response.data;
 });
Enter fullscreen mode Exit fullscreen mode

react-query package provides a hook to cache and fetch the data

useQuery is the hook that helps cache the data and fetch the data. In the above code, the second param is the callback or method usually of data fetching so I’ve defined axios.getrequest as the API call to JSON placeholder API endpoint and return the data.

useQuery simplifies the code by responding to the data key and the loading state as the isLoading key.

We doing nothing much just invoking API inside useQuery and getting all the responses in the data key and data fetching state as the isLoading key.

That’s it.

Rendering Table Columns & Rows

This is not as usual as the normal HTML table we often develop using other npm modules or UI library tables.

Tanstack provides hooks and a couple of methods to render each column and row to create a table.

Options Object:

  • When calling useReactTable, you provide an options object that includes essential properties like data and columns.
  • data: An array of objects representing the data to be displayed in the table.
  • columns: An array of column definitions that specify the structure and behaviour of each column in the table.

Creating Table Instance:

  • The table instance returned useReactTable encapsulates the table state and provides APIs to interact with the table.
  • It can be accessed to manage features like sorting, filtering, and pagination.

getState() API:

  • The getState() API allows you to access the current state of the table instance, enabling you to retrieve information about the table's state and configuration.

Column Definitions:

  • Column definitions are crucial for defining the structure of the table.
  • Each column is defined using the columnHelper.accessor method, specifying the header, cell content, and any additional properties.

Row Rendering:

  • The getCoreRowModel function is used to define how the rows should be displayed in the table.
  • It helps in customizing the appearance and behaviour of individual rows based on the data.

Rendering the Table:

  • Once the table instance is created, you can render the table by accessing the necessary elements like headerGroups, rows, and columns to display the table headers and data rows.

Following is the final code to render the table using the Tanstack table

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

const TanStackUsersTable = () => {
    const { data: allUsers, isLoading } = useQuery("allUsers", async () => {
        const response = await axios.get(
            "https://jsonplaceholder.typicode.com/users"
        );
        return response.data;
    });

    const columnHelper = createColumnHelper();

    const columns = [
        columnHelper.accessor("id", { header: "Id" }),
        columnHelper.accessor("name", { header: "Name" }),
        columnHelper.accessor("email", { header: "Email" }),
        columnHelper.accessor("phone", { header: "Phone" }),
        columnHelper.accessor("website", { header: "Website" }),
    ];

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

    return (
        <table className="p-10 m-10 w-10/12 mx-auto text-left border border-gray-100">
            <thead className="bg-gray-50 h-10">
                {table?.getHeaderGroups()?.map((headerGroup) => {
                    return (
                        <tr key={headerGroup?.id}>
                            {headerGroup?.headers?.map((header) => {
                                return (
                                    <th colSpan={header?.colSpan} key={header?.id}>
                                        {header?.isPlaceholder
                                            ? null
                                            : flexRender(
                                                    header?.column?.columnDef?.header,
                                                    header?.getContext()
                                              )}
                                    </th>
                                );
                            })}
                        </tr>
                    );
                })}
            </thead>
            {isLoading ? (
                <p>Loading users...</p>
            ) : (
                <tbody>
                    {table?.getCoreRowModel()?.flatRows.map((row) => (
                        <tr key={row.id} className="hover:bg-gray-50 h-16 cursor-pointer">
                            {row.getVisibleCells().map((cell) => {
                                return <td key={cell.id}>{cell?.getValue()}</td>;
                            })}
                        </tr>
                    ))}
                </tbody>
            )}
        </table>
    );
};

export default TanStackUsersTable;
Enter fullscreen mode Exit fullscreen mode

Here is the demo: iamshrey.me/projects/tanstack-table-demo

Few more examples

Performance

I was about to end this story but before code to github a thought about performance strike in my mind, since we are using a map inside a map time complexity will become O(n)² and this is not a performant code for production if the table is large and have pagination and nested columns.

If you are using the same code in production probably you need to do the following things

  • Add memoization
  • Add virtualization such as infinite scrolling
  • Add lazy loading if images are present in each row or even some
  • Avoid or cancel all re-rendering, simply track renders using dev tools
  • Add a caching layer to data fetching try to use only a few hooks

Data Caching and Avoid Rendering

useQuery is already caching the data so multiple API calls will be avoided using it and that will take care of re-rendering as well.

{table?.getHeaderGroups()?.map((headerGroup) => {
     return (
      <tr key={headerGroup?.id}>
       {headerGroup?.headers?.map((header) => {
        return (
         <th colSpan={header?.colSpan} key={header?.id}>
          {header?.isPlaceholder
           ? null
           : flexRender(
             header?.column?.columnDef?.header,
             header?.getContext()
             )}
         </th>
        );
       })}
      </tr>
     );
    })}
Enter fullscreen mode Exit fullscreen mode

This method might need some improvements if my columns are fixed datasets and have no filtering and other higher functionalities then simple static HTML elements will be more performance.

Remember, it’s the javascript that costs a lot in the browser to transpile so less JS more performance is the open-shut case answer.

Infinite Scrolling vs Pagination
I’ll always go for infinite scrolling as compared to pagination but it depends on the use case a lot by the product team.

But try to add any one of them instead of showcasing 50 columns in one go.

  • Conclusion

  • First, install the react-query package provided by tanstack

  • Data fetching method using API and useQuery hook

  • Create table instance using useReactTable hook provided by tanstack/react-table npm module

  • Defining table instance using columns definition and row definition

  • Rendering table columns and rows using a specific method of each tanstack row element

That's it for today.
See you in the next one
Shrey
iHateReading

Top comments (0)