Data Table
Also called table, data grid, grid, list view
A grid of rows and columns presenting structured records, where each column holds the same kind of value.
Example
Click a header to sort. Focus stays on the button and the new order is announced — sorting should never move you somewhere else.
| Invoice | |||
|---|---|---|---|
| INV-1042 | Acme Corp | $890 | Overdue |
| INV-1041 | Globex | $15,600 | Paid |
| INV-1040 | Initech | $320 | Draft |
| INV-1043 | Northwind | $2,400 | Paid |
Sorted by customer, ascending
The invoice number is a row header (th scope="row"), so a screen reader names the row before reading each cell in it.
When to use it
- Values need to be compared across records — prices, dates, counts, statuses read down a column.
- Each record has several attributes worth showing at once, and a card would hide most of them.
- The user scans for outliers: the failed run, the overdue invoice, the empty field.
- Sorting or filtering by a specific attribute is part of the job.
When not to
- Each record has two or three fields. A list is lighter and reads better on every screen size.
- The screen is narrow and the table is wide. Horizontal scrolling in a table is a last resort, not a design.
- Rows are really summaries of rich content — an article, a profile, a product. Cards suit those better.
- You are using it to lay out a form or a page. Tables are for data; the layout use is a habit left over from the nineties, and it wrecks the reading order for screen readers.
Trade-offs
- The densest readable way to show many records with many attributes.
- Column alignment does the comparison work for the reader without any extra effort.
- Sorting, filtering and selection all have well-understood conventions.
- Genuinely hard on narrow screens — every solution is a compromise.
- Easy to overload: twelve columns means nobody reads any of them.
- Accessible sorting, selection and keyboard navigation take real work to get right.
Accessibility
What this pattern needs in order to work for everyone.
- Keyboard
- Interactive cells must be reachable in a sensible order — left to right, top to bottom.
- Sortable headers are buttons: they must respond to Enter and Space, not just clicks.
- For very large grids, arrow-key cell navigation helps — but only implement it fully or not at all, since a half-built grid is worse than plain tab order.
- Roles and state
- Use a real `<table>` with `<th scope="col">` and `<th scope="row">`. Divs with grid CSS lose every association a screen reader relies on.
- Give the table a `<caption>` or `aria-label` — “Table” with no name is useless out of context.
- Put `aria-sort="ascending" | "descending"` on the header that is currently sorted, and only that one.
- When a row is selectable, the checkbox needs a label naming the row, not just “Select”.
- Focus
- Sorting must not move focus. The user pressed a header button and expects to stay there — announce the new order through a live region instead, or the change is invisible to anyone not watching the rows.
In the wild
- Stripe Dashboard — Payment tables stay readable at high density because the column set is deliberately small.
- GitHub Actions — Workflow runs are a table precisely because status and duration are compared down the column.