Search documentation

Search for a page or heading...

tablecn
0

Table components

The table, pagination, toolbar, search box and Columns menu, with their props.

The data-table block is the table UI from the preview. The CLI copies it into components/data-table/, or the folder you pass to --path (see Installation), so every file is yours to edit. The Radix UI, Base UI and React Aria versions have the same files and props.

Every component takes the table returned by useDataTable. They don't need a provider, and they don't depend on the filter: without a FilterProvider, the filter parts simply don't show.

Usage

components/orders-table.tsx
"use client"

import { FilterProvider, useBrowserUrlAdapter } from "@querycn/filter-react"
import { resetPagePatch, useDataTable } from "@querycn/table-react"

import { DataTable } from "@/components/data-table/data-table"
import { DataTablePagination } from "@/components/data-table/data-table-pagination"
import { DataTableSearch } from "@/components/data-table/data-table-search"
import { DataTableToolbar } from "@/components/data-table/data-table-toolbar"
import { FilterBuilder } from "@/components/filter/filter-builder"
import { FilterChips } from "@/components/filter/filter-chips"

export function OrdersPage({ orders }: { orders: Order[] }) {
  const adapter = useBrowserUrlAdapter()
  return (
    // A new filter starts again at page 1.
    <FilterProvider fields={orderFields} adapter={adapter} onApply={() => resetPagePatch()}>
      <OrdersTable orders={orders} />
    </FilterProvider>
  )
}

function OrdersTable({ orders }: { orders: Order[] }) {
  const table = useDataTable({
    data: orders,
    columns,
    getRowId: (order) => order.id,
    storageKey: "orders-table",
  })

  return (
    <div className="flex flex-col gap-3">
      <DataTableToolbar
        table={table}
        selectionActions={(rows) => <DeleteOrders ids={rows.map((row) => row.id)} />}
      >
        <DataTableSearch table={table} placeholder="Search orders…" />
        <FilterBuilder />
        <FilterChips />
      </DataTableToolbar>
      <DataTable table={table} className="max-h-[600px]" />
      <DataTablePagination table={table} />
    </div>
  )
}

The table reads the search, filter, sort and page from the same adapter, so one URL holds all of them: ?q=nguyen&status__eq=paid&sort=-amount&page=2.

DataTable

The table itself: sticky header, pinned columns, sortable, resizable and reorderable headers, and the loading, empty and error states.

Prop

Type

The table scrolls inside its own container. Give it a height (className="max-h-[600px]") and the header stays in view while the rows scroll. The container scrolls back to the top when the sort, page or filter changes. Other div props (id, aria-label…) go to the container.

The header cells are rendered for you (DataTableColumnHeader, one per column):

  • Click a sortable header to sort: ascending, descending, then off. Shift+click adds the column to the sort, and a small number shows its place.
  • Drag a header to reorder: the whole column follows the pointer and a line marks where it will land. The grip appears when it has keyboard focus.
  • Drag the header's end edge to resize: a line through the rows previews the new edge, and the width applies on release. Double-click the edge to fit the content. See Layout & persistence.
  • ⋯ (on hover or focus) or a right-click opens the column's menu: sort, pin, fit to content, color and hide. See The column menu.
  • meta.required adds a red * after the title. See Columns.

Cells of columns with meta.edit can be edited in place when useDataTable has onCellEdit: Enter, F2 or a double click opens the editor. See Inline editing.

createSelectionColumn

A checkbox column for row selection. The header checkbox selects the rows of the page:

Selection column
import { createSelectionColumn } from "@/components/data-table/data-table-selection-column"

const columns = [createSelectionColumn<Order>(), helper.accessor("customer", { header: "Customer" })]

It's pinned to the start and can't be hidden, sorted, resized or moved. Pass messages (the selection group) to translate its labels. Selection holds within one page: it clears when the page, sort or filter changes. Limit which rows can be selected with enableRowSelection in useDataTable.

DataTablePagination

The row count, a rows-per-page picker and page buttons. It renders nothing while there are no rows.

Prop

Type

  • Count: "1,234 rows", or "3 of 20 selected" while rows on the page are selected.
  • Rows per page offers the table's pageSizes (see URL state), and hides when every size would show every row.
  • Pages: first, previous, numbered pages with ellipses, next, last. In server mode, before the backend sends a total, only previous and next show; a full page is taken as a sign that another one follows.

DataTableViewOptions

The Columns button. Its popover lists the columns (hidden ones too) with a checkbox to show or hide each, a grip to reorder, and a ⋯ menu per column: pin to the start or end, unpin, fit to content, and a color. Under the list: Fit all columns and Reset layout.

Prop

Type

DataTableToolbar includes it. Render it yourself when you don't use the toolbar.

DataTableToolbar

Above the table: your controls on the left; Clear filters, reload and Columns on the right; and under them, the selection bar while rows are selected.

Prop

Type

DataTableSearch

A search box for the rows, written to the URL as ?q=. In client mode it looks in the searched columns, ignoring case and accents; in server mode it's sent to the backend. See The search box.

Prop

Type

  • Enter searches right away, without submitting a form around the box; Esc or the × clears, and focus stays in the box. An Esc that clears doesn't reach a Base UI or React Aria dialog around the box; Radix dialogs listen before the box does and close too.
  • The text is capped at 200 characters, like the URL.
  • A search changed elsewhere (back/forward, Clear filters) shows up in the box.
  • Changing the search goes back to page 1 and clears the selection.

DataTableResetFiltersButton

Clear filters: clears the applied filter and the search in one click. It only shows while one of them applies; without a FilterProvider, only the search counts. Props: table, messages, className.

The search goes back to page 1 by itself. For the filter, pass onApply={() => resetPagePatch()} to the FilterProvider, as in Usage: clearing it is a filter change like any other, and the page param is removed in the same URL update.

DataTableSelectionBar

The selected rows' count, your actions, and Clear selection. DataTableToolbar renders it when you pass selectionActions; render it yourself to put it elsewhere. It renders nothing while no row is selected. See Row selection. It only counts the selected rows of the page, so actions never receive a row the user can't see.

Prop

Type

row.original is the row's data, row.id its id from getRowId.

Keyboard and accessibility

  • Headers have aria-sort. To move a column from the keyboard, focus its grip (in the header or the Columns list), press Space or Enter, move with the arrow keys, and press Space again; Esc cancels. Each step is announced.
  • Resize handles are focusable separators: ← / → resize, Shift for bigger steps.
  • Pagination: moving to the first or last page disables the button that did it; focus moves to the current page's button instead of falling to the page.
  • Search is an <input type="search"> named by messages.search.label.
  • Toolbar: when a control that has focus goes away (Clear filters, Clear selection, an action that deletes the selected rows), focus moves to the toolbar.
  • Reload stays focusable while isRefreshing, marked as disabled.
  • Selection checkboxes are labelled "Select all" and "Select row".
  • Editable cells are focusable with Tab and described as "Press Enter to edit Column". Enter or F2 opens the editor, Esc closes it, and the focus comes back to the cell. A save error is a role="alert" under the cell. See Inline editing.
  • With virtualize, the table has aria-rowcount and each row aria-rowindex, so screen readers count every row of the page.