table
Show rows of data with sorting, paging and filters that stay in the URL.
What it is
@kitr/table is TanStack Table plus URL state. useUrlTableState() keeps sorting, paging and filters in the route's search params; parseTableSearch() validates them, so ?page=abc becomes page 1. Rows come from the server through query.
Runs in the browser. Made for router's validateSearch and navigate.
Install
kitr add tableFirst time? Run npm i -g @kitr/kitr once. Inside a project, the global kitr runs your project's own version.
Adds @kitr/table and TanStack Table. No provider, no config file. Every change.
Quick start
Three pieces: columns, a route that accepts table params, and the table. The columns:
// app/features/orders/columns.tsx
import type { ColumnDef, DataTableFeatures } from '@kitr/table'
export interface Order {
id: string
customer: string
total: number
status: 'open' | 'paid' | 'refunded'
}
export const columns: ColumnDef<DataTableFeatures, Order>[] = [
{ accessorKey: 'customer', header: 'Customer' },
{ accessorKey: 'total', header: 'Total', cell: ({ row }) => `€${row.original.total.toFixed(2)}` },
{ accessorKey: 'status', header: 'Status', enableSorting: false },
]
validateSearch: parseTableSearch on the route makes page, size, sort and filter typed search params. The page turns them into table state and hands them to the registry's <DataTable>:
// app/pages/orders/index.tsx
import { useSuspenseQuery } from '@kitr/query'
import { getRouteApi } from '@kitr/router'
import { useUrlTableState } from '@kitr/table'
import { DataTable } from '#components/ui/data-table'
import { columns } from '#app/features/orders/columns'
import { ordersPageQuery } from '#app/features/orders/queries'
const route = getRouteApi('/orders')
export default function OrdersPage() {
const search = route.useSearch()
const navigate = route.useNavigate()
const { data } = useSuspenseQuery(ordersPageQuery.options(search))
const urlState = useUrlTableState({
search,
setSearch: (patch) => navigate({ search: (prev) => ({ ...prev, ...patch }) }),
})
return <DataTable columns={columns} data={data.rows} rowCount={data.total} {...urlState} />
}
Click the Total header. The URL becomes /orders?sort=-total, the loader fetches that page, and the table shows the largest orders first. Reload and it stays sorted. Next in the guide: Show your first sortable table.
Built on
kitr adds the URL state hook, the search param parser and the feature set for the data table component.
Problems it solves
- Table state lost on reload. Keep sorting, paging and filters in the URL.
- Crashes from hand-edited URLs. Validate the URL.
- Thousands of rows in the browser. Fetch one page from the server.
- Filters per column. Filter by a column.
- Small datasets that need no API paging. Sort and page in the browser.
- Styling someone else's table markup. Render your own markup.
Further reading
- Guide.
- API reference.
- TanStack Table docs.
- Router and Query.