use UI

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.

Recent invoices, sortable by customer, amount and status
Invoice
INV-1042Acme Corp$890Overdue
INV-1041Globex$15,600Paid
INV-1040Initech$320Draft
INV-1043Northwind$2,400Paid

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.

Related tools