Skip to Content
Components
DataTable ALPHA

DataTable

Easy to use table and datagrids built using TanStack Table.

Documentation

Install

Install the required peer dependencies to start using this component:

npm install @tanstack/react-table

Usage

Use the table prop to pass a table instance returned from useReactTable() hook. At minimum make sure to include columns, data, and getCoreRowModel when using the hook.

First Name
Last Name
Status
Amount
ArthurMorgansuccess$20
JohnMarstonprocessing$12.75
MicahBellfailed$1.25
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), }); return ( <DataTable table={table}> <DataTableBody /> </DataTable> ); }

Anatomy

import { DataTable, DataTableBody, DataTableFooter } from "@optiaxiom/react"; export default () => ( <DataTable> <DataTableBody /> <DataTableFooter /> </DataTable> );

Vertical scrolling

Headers in DataTable are sticky by default. Set a height on the component to allow scrolling vertically when a large number of rows are present.

ID
First Name
Last Name
Amount
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), state: { pagination: { pageIndex: 0, pageSize: data.length } }, }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> </DataTable> ); }

Column pinning

Follow the column pinning guide and use the columnPinning option to pin columns either to the left or right side of the table.

ID
Username
Email
First Name
Last Name
Job Title
Amount
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), state: { columnPinning: { left: ["id", "username"], }, pagination: { pageIndex: 0, pageSize: data.length }, }, }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> </DataTable> ); }

Row selection

Follow the row selection guide to allow selecting and highlighting rows.

ID
First Name
Last Name
Amount
1EdgardoLubowitz$696.47
2HenriPaucek-Kshlerin$719.47
3EverardoKessler$392.12
4GranvilleDeckow$59.67
5JustynNicolas$175.45
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), }); return ( <DataTable table={table}> <DataTableBody /> </DataTable> ); }

Row actions

Use the TableActions component to show dropdown menus within rows that are only visible when the user hovers over the row or navigates to the menu using keyboards.

First Name
Last Name
Amount
EdgardoLubowitz$696.47
HenriPaucek-Kshlerin$719.47
EverardoKessler$392.12
GranvilleDeckow$59.67
JustynNicolas$175.45
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), }); return ( <DataTable table={table}> <DataTableBody /> </DataTable> ); }

Pagination

DataTable supports both client-side and server-side pagination. Follow the pagination guide to learn how to implement pagination with TanStack Table.

Client side

Simply provide the getPaginationRowModel and pagination options to use client-side pagination.

ID
First Name
Last Name
Amount
1EdgardoLubowitz$696.47
2HenriPaucek-Kshlerin$719.47
3EverardoKessler$392.12
4GranvilleDeckow$59.67
5JustynNicolas$175.45
6ModestaReichert$849.44
7FrancescaGreenholt$322.96
8AudreanneKeeling$630.98
9HerminioKautzer$493.69
10TyreekMuller$893.39

1 - 10 of 50

import { DataTable, DataTableBody, DataTableFooter, } from "@optiaxiom/react/unstable"; import { getCoreRowModel, getPaginationRowModel, useReactTable, } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> <DataTableFooter /> </DataTable> ); }

Server side

To implement manual server-side pagination we can use manualPagination and rowCount. Then combine it with onPaginationChange and state.pagination to control pagination and react to changes.

ID
First Name
Last Name
Amount
1EdgardoLubowitz$696.47
2HenriPaucek-Kshlerin$719.47
3EverardoKessler$392.12
4GranvilleDeckow$59.67
5JustynNicolas$175.45
6ModestaReichert$849.44
7FrancescaGreenholt$322.96
8AudreanneKeeling$630.98
9HerminioKautzer$493.69
10TyreekMuller$893.39

1 - 10 of 50

import { DataTable, DataTableBody, DataTableFooter, } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { useMemo, useState } from "react"; import { columns } from "./columns"; import { data } from "./data"; /** * Simulate fetching data from server. */ const fetchData = ({ pageIndex = 0, pageSize = 10 }) => data.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize); export function App() { const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10, }); const table = useReactTable({ columns, data: useMemo(() => fetchData(pagination), [pagination]), getCoreRowModel: getCoreRowModel(), manualPagination: true, onPaginationChange: setPagination, rowCount: data.length, state: { pagination }, }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> <DataTableFooter /> </DataTable> ); }

Loading state

Set the loading prop to true to show a loading skeleton while your table data is loading.

ID
First Name
Last Name
Amount
import { Flex, Switch } from "@optiaxiom/react"; import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { useState } from "react"; import { columns } from "./columns"; export function App() { const [data] = useState([]); const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), }); const [loading, setLoading] = useState(true); return ( <Flex alignItems="start" maxW="full"> <Switch checked={loading} onCheckedChange={setLoading}> Loading </Switch> <DataTable maxH="xs" table={table}> <DataTableBody loading={loading} /> </DataTable> </Flex> ); }

Sorting

DataTable supports both client-side and server-side sorting. Follow the sorting guide to learn how to implement sorting with TanStack Table.

Client side

Simply provide getSortedRowModel on table and enableSorting on each column you’d like to be sortable to use client-side sorting.

ID
1EdgardoLubowitz$696.47
2HenriPaucek-Kshlerin$719.47
3EverardoKessler$392.12
4GranvilleDeckow$59.67
5JustynNicolas$175.45
6ModestaReichert$849.44
7FrancescaGreenholt$322.96
8AudreanneKeeling$630.98
9HerminioKautzer$493.69
10TyreekMuller$893.39
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> </DataTable> ); }

Server side

To implement manual server-side sorting we can use manualSorting and enableSorting. Then combine it with onSortingChange and state.sorting to control sorting and react to changes.

ID
8AudreanneKeeling$630.98
1EdgardoLubowitz$696.47
3EverardoKessler$392.12
7FrancescaGreenholt$322.96
4GranvilleDeckow$59.67
2HenriPaucek-Kshlerin$719.47
9HerminioKautzer$493.69
5JustynNicolas$175.45
6ModestaReichert$849.44
10TyreekMuller$893.39
import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, type SortingState, useReactTable, } from "@tanstack/react-table"; import { useMemo, useState } from "react"; import { columns } from "./columns"; import { data } from "./data"; export function App() { const [sorting, setSorting] = useState([ { desc: false, id: "firstName", }, ]); const table = useReactTable({ columns, data: useMemo(() => sortData(sorting), [sorting]), getCoreRowModel: getCoreRowModel(), onSortingChange: setSorting, state: { sorting }, }); return ( <DataTable maxH="xs" table={table}> <DataTableBody /> </DataTable> ); } /** * Simulate sorting data from server. */ const sortData = (sorting: SortingState) => data.toSorted((a, b) => { for (const state of sorting) { if ( !( state.id === "amount" || state.id === "firstName" || state.id === "lastName" ) ) { return 0; } const result = (state.desc ? -1 : 1) * a[state.id].localeCompare(b[state.id], undefined, { numeric: true }); if (result !== 0) { return result; } } return 0; });

Virtualized

Table automatically uses virtualized rendering when rows and columns cross a certain threshold to improve rendering performance.

Columns will be virtualized when there are more than 20 columns in a table and rows will be virtualized when there are more than 20 rows rendered or if columns are also being virtualized.

Timer: 0s (to simulate re-rendering)

Column 1
Column 2
Column 3
Column 4
Column 5
Column 6
Column 7
Column 8
Column 9
Column 10
Column 11
Column 12
Column 13
Column 14
Column 15
Column 16
Column 17
Column 18
Column 19
Column 20
Column 21
Column 22
Column 23
Column 24
Column 25
Column 26
Column 27
Column 28
Column 29
Column 30
Column 31
Column 32
Column 33
Column 34
Column 35
Column 36
Column 37
Column 38
Column 39
Column 40
Column 41
Column 42
Column 43
Column 44
Column 45
Column 46
Column 47
Column 48
Column 49
Column 50
import { faker } from "@faker-js/faker"; import { Flex, Text } from "@optiaxiom/react"; import { DataTable, DataTableBody } from "@optiaxiom/react/unstable"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { useInViewTimer } from "./useInViewTimer"; faker.seed(123); const cols = 50; const rows = 500; const data = Array.from({ length: rows }, () => Object.fromEntries( Array.from({ length: cols }, (_, index) => [ `col${index}`, faker.book.title(), ]), ), ); const columns = Array.from({ length: cols }, (_, index) => ({ accessorKey: `col${index}`, header: `Column ${index + 1}`, })); export function App() { const table = useReactTable({ columns, data, getCoreRowModel: getCoreRowModel(), state: { pagination: { pageIndex: 0, pageSize: data.length } }, }); /** * A simple hook to re-render the table when it is visible on the screen. */ const [count, ref] = useInViewTimer(); return ( <Flex maxW="full" ref={ref}> <Text>Timer: {count}s (to simulate re-rendering)</Text> <DataTable maxH="sm" table={table}> <DataTableBody /> </DataTable> </Flex> ); }

Props

DataTable

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

table*

Pass the table instance returned from useReactTable() hook.

Table<any>

DataTableBody

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

estimatedRowHeight

The estimated height of rows in pixels when virtualization is enabled.

number

Default: 52

loading

Indicates if the table is loading

false | true

DataTableFooter

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

pageSizeOptions

string[]

Default: ["10", "20", "30", "50", "100"]

showPageSizeOptions

false | true

TableActions

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

Changelog

0.3.0

  • Added component
Last updated on