Columns
Define what the table shows, and what users can do with each column.
Columns are TanStack Table column definitions, with a few extra options tablecn reads. Build them with createDataTableColumnHelper, which types those options for you:
"use client"
import { createDataTableColumnHelper } from "@querycn/table-react"
import { createSelectionColumn } from "@/components/data-table/data-table-selection-column"
interface Order {
id: string
customer: { name: string }
status: "paid" | "pending"
amount: number
createdAt: string
}
const column = createDataTableColumnHelper<Order>()
export const orderColumns = [
createSelectionColumn<Order>(),
column.accessor("customer.name", {
header: "Customer",
size: 200,
meta: { defaultPinned: "start" },
}),
column.accessor("status", { header: "Status" }),
column.accessor("amount", {
header: "Amount",
sortDescFirst: true,
cell: ({ getValue }) => getValue().toFixed(2),
}),
column.accessor("createdAt", { header: "Created", meta: { defaultHidden: true } }),
column.display({
id: "actions",
header: () => <span className="sr-only">Actions</span>,
meta: { label: "Actions" },
enableHiding: false,
cell: ({ row }) => <OrderActions order={row.original} />,
}),
]Define columns outside your components
Put the array at module scope, or wrap it in useMemo. A new array on every render rebuilds the table's columns.
Prefer writing the objects by hand? Type each one as DataTableColumnDef<Order>.
Column ids
Every column has an id. It's what appears in the URL (?sort=-customer_name), in the saved layout, and in data-column-id on every cell. It comes from, in order:
- an explicit
id; - the accessor key, with dots replaced by
_:customer.namebecomescustomer_name; - a string
header.
Renaming a column's id breaks links and saved layouts that use the old one. See Layout for how to handle that.
Starting layout
meta sets how a column looks the first time a user sees the table, and after they press Reset layout:
column.accessor("customer.name", { header: "Customer", meta: { defaultPinned: "start" } })
column.accessor("createdAt", { header: "Created", meta: { defaultHidden: true } })Prop
Type
Once users change the layout, their saved layout wins. See Layout.
What users can do
By default, users can sort, move, resize, hide, pin and color every column. Each has its own page; here are the switches in one place. Turn a feature off for the whole table in useDataTable, or for one column in its definition:
| Feature | Whole table (useDataTable) | One column | See |
|---|---|---|---|
| Sort | enableSorting: false | enableSorting: false | Sorting |
| Move (drag and drop) | enableColumnOrdering: false | meta.enableOrdering: false | Moving columns |
| Resize and fit | enableColumnResizing: false | enableResizing: false | Resizing |
| Hide | — | enableHiding: false | Hiding columns |
| Pin | — | enablePinning: false | Pinning |
| Color | — | — | Column colors |
const table = useDataTable({
data,
columns,
getRowId: (order) => order.id,
enableColumnOrdering: false,
enableColumnResizing: false,
})A table-wide false wins over a column's true. Any other TanStack column option works too.
Sorting and selection
Sorting has its own page, Sorting, and so does the checkbox column, Row selection.
Grouped headers
Group columns under a shared header with column.group({ header, columns }). Sorting, resizing, pinning and hiding work on the inner columns. Dragging columns is turned off while there's more than one header row, since moving a column out of its group would break the group.