useTable
Connect filter forms to data tables with type-safe queries and automatic state management.
Overview
useTable combines the power of useForm with TanStack Query to create a seamless connection between filter forms and data tables. It manages filter state, query execution, and table props automatically.
Key Features
- Built on useForm - All type-safe form features from
useFormare included - Automatic query management - Filters are synced with TanStack Query
- Type-safe data fetching - Full type inference for filter values and query results
- Built-in pagination - Server-side pagination with automatic state management
- Colocated query definitions - Define query options separately with
createTableQueryOptions
Usage
See the Basic example for a live demo.
import React from "react";
import { , , , } from "antd";
import { } from "@tanstack/react-query";
import { } from "zod";
import { } from "@dyrhoi/antd-forge";
type = {
: number;
: string;
};
declare function (: { ?: string }): <{ : []; : number }>;
const = .({
: .().(),
});
function () {
const { , , } = ({
: ,
: ({ filters, }) => ({
: ["users", ],
: () => (),
: () => ({ : ., : . }),
}),
});
// `tableProps.dataSource` is fully typed as `User[]`
const dataSourceType = .;
return (
<>
< {...} ="inline">
< ={["search"]} ="Search">
< />
</>
< ="primary" ="submit">
Search
</>
</>
< {...} ={[
{
: "ID",
: "id",
: "id",
},
{
: "Name",
: "name",
: "name",
},
]} ="id" />
</>
);
}Without TanStack Query
If you're not using TanStack Query, you can use the simpler search function instead. See the Basic search example for a live demo.
const { formProps, FormItem, tableProps } = useTable({
validator: schema,
search: async ({ filters, pagination }) => {
const data = await fetchUsers(filters, pagination.current, pagination.pageSize);
return { data: data.items, total: data.count };
},
});Pagination
useTable provides built-in server-side pagination. The search or queryOptions function receives pagination state, and tableProps automatically wires up the table's pagination UI.
How It Works
- Your search function receives
{ filters, pagination: { current, pageSize } } - Return
{ data, total }wheretotalis the total count (not page count) tableProps.paginationis automatically configured with the current state- Page resets to 1 when filters change or page size changes
Custom Initial Values
const { tableProps, pagination } = useTable({
validator: schema,
search: searchFn,
pagination: {
initial: {
pageSize: 25, // Default is 10
current: 1,
},
},
});
// Read current pagination state
console.log(pagination.current, pagination.pageSize, pagination.total);Using Pagination in Queries
const { tableProps } = useTable({
validator: schema,
queryOptions: ({ filters, pagination }) =>
queryOptions({
queryKey: ["users", filters, pagination],
queryFn: () => fetchUsers({
...filters,
page: pagination.current,
limit: pagination.pageSize,
}),
select: (data) => ({ data: data.items, total: data.totalCount }),
}),
});Client-Side Pagination
By default, useTable uses server-side pagination where you handle pagination in your API. For smaller datasets, you can use client-side pagination where Ant Design's Table handles pagination from the full dataset. See the Client Pagination example for a live demo.
With client mode:
- Your search/query function receives no pagination props - just filters
- Return a plain array instead of
{ data, total } - Ant Design's Table automatically paginates from the full
dataSource - The
pageSizesetting controls how many rows display per page
// Client pagination - fetch all data, let Table handle pagination
const { tableProps } = useTable({
validator: schema,
pagination: { mode: "client", initial: { pageSize: 10 } },
search: async ({ filters }) => {
// Note: no `pagination` in props for client mode
const allItems = await fetchAllUsers(filters);
return allItems; // Return array directly, not { data, total }
},
});Client Pagination with TanStack Query
When using queryOptions with client pagination, use createTableQueryOptions with the client config:
import { createTableQueryOptions, useTable } from "@dyrhoi/antd-forge";
import { queryOptions } from "@tanstack/react-query";
// Define query options with client pagination mode
const usersQueryOptions = createTableQueryOptions<Filters, User>({
pagination: { mode: "client" },
})(({ filters }) =>
queryOptions({
queryKey: ["users", filters],
queryFn: () => fetchAllUsers(filters), // Returns User[]
// No need to transform to { data, total }
}),
);
function UsersTable() {
const { tableProps } = useTable({
validator: schema,
queryOptions: usersQueryOptions,
pagination: { mode: "client", initial: { pageSize: 10 } },
});
// ...
}When to Use Client vs Server Pagination
| Aspect | Server Mode (default) | Client Mode |
|---|---|---|
| Best for | Large datasets | Small datasets (under 1000 rows) |
| Data fetching | Fetch per page | Fetch all at once |
| Return type | { data, total } | TData[] |
| Pagination logic | Your API | Ant Design Table |
| Network usage | Lower per request | Higher initial load |
Auto Submit
Since useTable is built on useForm, it inherits the autoSubmit option. This is particularly useful for filter forms where you want the table to update as the user types—no submit button required. See the Auto Submit example for a live demo.
For full documentation on modes and configuration, see useForm - Auto Submit.
const { formProps, FormItem, tableProps } = useTable({
validator: schema,
autoSubmit: { mode: "auto", debounce: 400 },
search: ({ filters, pagination }) => fetchData(filters, pagination),
});Examples
Basic
Using queryOptions with TanStack Query for data fetching.
Basic Search
Using the simpler search function without TanStack Query.
Client Pagination
Fetching all data and letting Ant Design Table handle pagination.
Auto Submit
Automatically updating the table as the user types in the filter form.