Docs / Guide: Data Tables That Survive Real Data
Sorting, filtering and pagination wired honestly — and the line where a table stops being the right tool.
A sortable column sorts the whole dataset, not the visible page, and says so: aria-sort on the header, the direction in the label, a stable order when values tie. One column sorts at a time unless the UI can explain two. Numbers sort as numbers, dates as dates, and names the way the locale expects — a table that sorts "10" before "9" has told the reader it cannot be trusted with their data.
The default sort is the order the reader would choose: newest first for logs, name for people, status for work queues. If the table needs a manual to open, the default is wrong.
A search input, a status select, and the table — three parts and forty lines of script. Debounce the input, filter the rows in the DOM for the sizes a page should carry, and let the count in the footer describe the filtered set, not the total. Filters are state the reader owns: keep them in the URL when the page is shareable, and never reset them on an unrelated action.
When a filter excludes everything, the empty state says what was excluded and offers the one click that clears it. An empty table with no explanation reads as data loss.
The footer counts what is shown out of what there is — "21–40 of 312" — and the page size is a choice the reader can change. Paginate the filtered set, keep the sort across pages, and if the data changes under the reader, say so rather than silently renumbering. Infinite scroll is a reading pattern, not a working pattern: a table someone acts on needs pages they can return to.
The honest limit: a few thousand rows in the DOM is where a plain table stops being kind — layout, find-in-page and screen readers all pay. Past that, paginate on the server and let the part be the presentation of one page of data, which it does unchanged. A virtualized grid with cell editing and frozen panes is a different instrument; that is framework territory, and this guide will not pretend otherwise.
Most internal tables never get near the limit. Build for the data you have, and let the footer count tell you when the day comes.
Introduction · Getting Started · CLI · Theming · Dark Mode · Accessibility · Installation · Plain HTML · Astro · Laravel · React & Next.js · JavaScript · Guide: Building a Dashboard · Guide: Building a Settings Page · Guide: Building a Pricing Page · Guide: Sign-in & Account Pages · Guide: Forms, For Real · AI Prompts · Why Vanilla · The Mold · Anatomy of a Part · Coming from Tailwind · KATA vs shadcn/ui · FAQ · Registry · Forms · MCP Server · RTL · Contributing · Changelog