What is Data table?
Data table: A data table is an interactive grid of records with columns you can sort, filter, paginate and select, built for scanning and acting on structured data.
A data table starts as an HTML table and adds the controls people need to work with many rows: sortable column headers, filters and search, pagination or virtualization, row selection with bulk actions, and per-row actions. Admin panels, billing pages, CRMs and logs are built around them. The goal is fast comparison down a column and fast action on a row, so typography, alignment and density matter as much as features.
Anatomy
- Toolbar: search, filters, column visibility and saved views above the table.
- Header row: column labels, sort controls and a select-all checkbox.
- Body rows: cells aligned by data type, with numbers right-aligned in
tabular-numsso digits line up. - Row actions revealed on hover and on keyboard focus, never on hover alone.
- Footer: row count, page size and pagination.
- States: loading (skeleton rows), empty, error and filtered-to-nothing.
Accessibility
Keep the native table semantics: table, thead, th scope="col", and a caption or aria-label that names the table. Make each sortable header contain a button, and put aria-sort="ascending" or "descending" on the th of the one column currently sorted. Checkboxes need labels such as "Select row Acme Inc". Do not add role="grid" unless you also implement the full grid keyboard model, where arrow keys move between cells. A table with ordinary buttons and links inside it, reached with Tab, is easier to get right and works well with screen reader table navigation.
How to build one in React
MiniDev's DataTable takes typed column definitions and handles sorting with aria-sort, selection with an indeterminate header, sticky headers, density and loading:
import { DataTable, type Column } from "@/components/ui/data-table"
type Invoice = { id: string; customer: string; amount: number; due: Date }
const usd = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })
const columns: Column<Invoice>[] = [
{ id: "customer", header: "Customer", cell: (r) => r.customer, sortValue: (r) => r.customer },
{ id: "amount", header: "Amount", align: "right", cell: (r) => usd.format(r.amount), sortValue: (r) => r.amount },
{ id: "due", header: "Due", cell: (r) => r.due.toLocaleDateString(), sortValue: (r) => r.due },
]
export function Invoices({ rows }: { rows: Invoice[] }) {
return <DataTable columns={columns} data={rows} getRowId={(r) => r.id} selectable caption="Invoices" />
}
Common mistakes
- Sorting only the current page when data is paginated on the server. Send sort and filter to the query.
- Losing selection when the user changes page or filter without telling them.
- Wide tables that scroll the whole page sideways. Scroll the table container and pin the identifying column with StickyColumn.
- Hover-only row actions. Reveal them on
:focus-withintoo, or keyboard users never find them. - Mixed alignment. Left-align text, right-align numbers and their headers, and put units in the header instead of every cell.
Decide early where the data lives. Up to a few thousand rows, sorting and filtering in the browser is simple and fast. Beyond that, move sort, filter and pagination to the server and keep them in the URL so views can be shared and restored.
See the full set in the data tables category and the SaaS dashboard guide.
Data TableuiReact data table with sortable columns, row selection, a floating bulk actions bar, hover row actions, sticky header, three densities and skeleton loading.npx shadcn@latest add ui.minidev.pro/r/data-table.jsonTable ToolbaruiToolbar above a data table with a search input on the left and a right slot for filters, view toggles or export buttons. Wraps cleanly on narrow screens.npx shadcn@latest add ui.minidev.pro/r/table-toolbar.json