Skip to main content

React Table

refine offers a TanStack Table adapter with @pankod/refine-react-table that allows you to use the TanStack Table library with refine. Thus, you can manage your server-side data fetching operations.

All of TanStack Table's features are supported and you can use all of the TanStack Table's examples with no changes just copy and paste them into your project.

Installation

Install the @pankod/refine-react-table library.

npm i @pankod/refine-react-table

Basic Usage

In this documentation, we'll step-by-step create an example of a headless table with sorting, filtering, and pagination capabilities.

Let's say you have a endpoint that returns the following data.

https://api.fake-rest.refine.dev/posts
[
{
"id": 182,
"title": "A aspernatur rerum molestiae.",
"content": "Natus molestias incidunt voluptatibus. Libero delectus facilis...",
"status": "published",
"createdAt": "2021-04-18T00:09:11.607Z"
},
{
"id": 989,
"title": "A molestiae vel voluptatem enim.",
"content": "Voluptas consequatur quia beatae. Ipsa est qui culpa deleniti...",
"status": "draft",
"createdAt": "2020-01-28T02:57:58.892Z"
}
]

Create <PostList> component

We simply create a <PostList> component and pass to the <Refine> component as a resource. All the implementation we will do from now on will be in the <PostList> component.

src/posts/list.tsx
export const PostList: React.FC = () => {
return <></>;
};
src/App.tsx
import { Refine } from "@pankod/refine-core";
import routerProvider from "@pankod/refine-react-router-v6";
import dataProvider from "@pankod/refine-simple-rest";
import "./App.css";

import { PostList } from "pages/posts/list";

const App: React.FC = () => {
return (
<Refine
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
routerProvider={routerProvider}
resources={[{ name: "posts", list: PostList }]}
/>
);
};

export default App;
Show basic table style

src/App.css
table {
border-spacing: 0;
border: 1px solid black;
}

table th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
}

table tr:last-child td {
border-bottom: 0;
}

table th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
}

table th:last-child,
td:last-child {
border-right: 0;
}

Create basic table

Firts, we need to import the useTable hook from the @pankod/refine-react-table library.

src/posts/list.tsx
import { useTable } from "@pankod/refine-react-table";

export const PostList: React.FC = () => {
return <></>;
};

Define columns what we want to display in the table. Then, return the headless table with using the useTable hook.

info

useTable does not expect any data prop to be passed to it. It will fetch data from the data provider by resource.

src/posts/list.tsx
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
createdAt: string;
}

export const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(
() => [
{
id: "id",
header: "ID",
accessorKey: "id",
},
{
id: "title",
header: "Title",
accessorKey: "title",
},
{
id: "status",
header: "Status",
accessorKey: "status",
},
{
id: "createdAt",
header: "CreatedAt",
accessorKey: "createdAt",
},
],
[],
);

const { getHeaderGroups, getRowModel } = useTable({ columns });

return (
<table>
<thead>
{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>
{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>
);
};
note

This example is the same as the basic example in the TanStack Table documentation.

Refer to the basic example of TanStack Table.

Basic Table

Pagination

TanStack Table provides a bunch of methods that we can use to control the pagination. For example, we can use the setPageSize method to set the current pageSize. Every change in the pageSize and pageIndex will trigger a new request to the data provider.

Refer to the TanStack Table Pagination API documentation for detailed information.

info

useTable hook from @pankod/refine-react-table sets manualPagination to true by default to handle the pagination. If you set hasPagination to false in refineCoreProps property in the useTable config, it will disable the server-side pagination and it will let you handle the pagination in the client side.

src/posts/list.tsx
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
createdAt: string;
}

export const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(...); // Defined in the previous section

const {
getHeaderGroups,
getRowModel,
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
} = useTable({ columns });

return (
<>
<table>
<thead>
{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>
{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>
// Pagination can be built however you'd like. // This is just a very
basic UI implementation: //highlight-start
<div>
<button
onClick={() => setPageIndex(0)}
disabled={!getCanPreviousPage()}
>
{"<<"}
</button>
<button
onClick={() => previousPage()}
disabled={!getCanPreviousPage()}
>
{"<"}
</button>
<button onClick={() => nextPage()} disabled={!getCanNextPage()}>
{">"}
</button>
<button
onClick={() => setPageIndex(getPageCount() - 1)}
disabled={!getCanNextPage()}
>
{">>"}
</button>
<span>
<div>Page</div>
<strong>
{getState().pagination.pageIndex + 1} of{" "}
{getPageCount()}
</strong>
</span>
<span>
| Go to page:
<input
type="number"
defaultValue={getState().pagination.pageIndex + 1}
onChange={(e) => {
const page = e.target.value
? Number(e.target.value) - 1
: 0;
setPageIndex(page);
}}
/>
</span>
<select
value={getState().pagination.pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</>
);
};
Pagination Table

Sorting

TanStack Table provides a bunch of methods that we can use to control the sorting. For example, we can use the setColumnOrder method to set the current sorting value. Every change in the sorting state will trigger a new request to the data provider.

Refer to the useSortBy documentation for detailed information.

src/posts/list.tsx
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
createdAt: string;
}

export const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(...); // Defined in the previous section

const {
getHeaderGroups,
getRowModel,
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
} = useTable({ columns });

return (
<>
<table>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<div
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{{
asc: " 🔼",
desc: " 🔽",
}[
header.column.getIsSorted() as string
] ?? null}
</div>
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{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>
// Pagination defined in the previous section
</>
);
};
Sortable Table

Filtering

TanStack Table provides a bunch of methods that we can use to control the filtering. For example, we can use the setColumnFilters method to set the current columnFilters value. Every change in the filter will trigger a new request to the data provider.

You can specify which field will be filtered with which filter operator with the filterOperator property in the meta object. filterOperator must be a CrudOperators type.

src/posts/list.tsx
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
createdAt: string;
}

export const PostList: React.FC = () => {
const columns = React.useMemo<ColumnDef<IPost>[]>(
() => [
{
id: "id",
header: "ID",
accessorKey: "id",
},
{
id: "title",
header: "Title",
accessorKey: "title",
meta: {
filterOperator: "contains",
},
},
{
id: "status",
header: "Status",
accessorKey: "status",
meta: {
filterOperator: "contains",
},
},
{
id: "createdAt",
header: "CreatedAt",
accessorKey: "createdAt",
},
],
[],
);

const {
getHeaderGroups,
getRowModel,
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
} = useTable({ columns });

return (
<>
<table>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<>
<div
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(
header.column.columnDef
.header,
header.getContext(),
)}
{{
asc: " 🔼",
desc: " 🔽",
}[
header.column.getIsSorted() as string
] ?? null}
</div>
<div>
<input
value={
(header.column.getFilterValue() as string) ??
""
}
onChange={(e) =>
header.column.setFilterValue(
e.target.value,
)
}
/>
</div>
</>
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{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>
// Pagination defined in the previous section
</>
);
};
Filtering

API Reference

Properties

External Props

It also accepts all props of TanStack Table.

Type Parameters

PropertyDesriptionTypeDefault
TDataResult data of the query. Extends BaseRecordBaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpErrorHttpError

Return values

PropertyDescriptionType
refineCoreThe return values of the useTable in the coreUseTableReturnValues
Tanstack Table Return ValuesSee TanStack Table documentation

Live StackBlitz Example