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.
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
- Next.js for the frontend framework
- Vercel for deployment
- Firebase as Database
- Github as version control
- Tailwind CSS as a styling library
- Material UI and Mantine.dev as UI library
- 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
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.
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
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
- Column Ordering: ordering the table columns
- Column Pinning: Pinning the table columns
- Column Sizing: Sizing the table columns
- Column Visibility: Toggle the show/hide of the table column
- Filter
- Sorting
- Grouping
- Expanding
- Pagination
- Row Selection
Examples
The Tanstack website for this headless table does have an example section.
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';
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 })
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;
});
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;
});
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.get
request 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;
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>
);
})}
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)