Table
Concept
A component that lays data out in rows and columns so values of the same kind can be compared against each other.
Why it exists
There are several ways to display a list, and only one layout makes values comparable. Numbers of the same kind have to line up vertically before anyone's eye can run down them and tell large from small. Any other arrangement turns comparison into thirty separate acts of reading and remembering.
Pagedeck's admin screen taught the team that difference. The team account list started out as a grid of boxes, each one carrying the team name, headcount, storage used and billing status. It looked tidy. Then Sofia, who ran operations, asked which team was using the most storage, and there was no way to answer. The numbers sat in a different position in every box, so answering meant reading thirty of them one at a time. Replacing the boxes with rows and columns brought the same question down to three seconds: click the storage column to sort.
So this component exists for comparison, not for volume. A list nobody needs to compare reads better grouped into cards; a list that exists to be compared belongs in rows and columns.
How it works
One row is one record and one column is one attribute. Comparison comes out of values of the same attribute lining up vertically.
- The top row holds the column names. It has to look different from the rest and stay pinned as the page scrolls, or by the twentieth row nobody knows what the number they're reading refers to.
- Alignment follows the type of value. Text goes left, numbers go right. Right-aligned numbers put the digits in a vertical line, which makes the gap between 1,200 and 980 visible before anyone reads either figure.
- Sorting happens by clicking a column name. Show which column is sorted and in which direction with an arrow. Without that indicator, users don't know what their own click just did, and they click again to check.
- Put the row selector and the actions for selected rows together. A checkbox column on the left, and a bar above the table showing the number selected plus the bulk actions once anything is. Selecting eight rows and then hunting for where the delete button went is the failure this arrangement prevents.
- Use a dash for missing values rather than an empty cell. Blank doesn't distinguish a zero from a value that hasn't arrived from a rendering bug, and support threads about "the storage column is broken" usually turn out to be rows where the value is legitimately absent.
- Split long lists into pages or extend them as the user scrolls. Pagination keeps a position worth returning to, which matters when someone works through a queue over several sittings; continuous loading suits scanning for one thing.
Column count sets the limit of this component. Past about seven, horizontal scrolling appears, and once the view is scrolled sideways there's nothing left to tell you which team a number belongs to. Screens that genuinely need more columns pin the first one — the identifying column — to the left edge.
Where it shows up
It belongs on screens where values get compared, or where one record gets picked out and acted on. Four places account for most of them.
- Admin screens. Account lists, billing history, permission lists: dozens to thousands of records where the job is finding one and doing something to it. These screens also tend to need filtering and bulk actions, both of which attach naturally to rows.
- History and audit logs. Who changed what and when, laid out in time order, with sorting by timestamp as the default behaviour and the newest entry at the top.
- Plan comparisons. Features down the side and plans across the top makes the differences between tiers immediately readable. Here the values are presence and absence rather than numbers, so using one consistent mark decides how fast it reads.
- The lower half of a dashboard. Charts up top carry the trend, and the individual records behind those numbers sit underneath in a table.
Narrow screens don't take this layout as-is. Seventeen columns in a 400-pixel viewport are unreadable under any arrangement, so each row becomes a stacked card, or two or three columns stay and the rest move to a detail view reached by tapping the row. Splitting groups of columns across tabs also works.
It's worth considering readers who aren't looking at the screen. Mark the header row as a header row and a screen reader announces the column name with each cell. Without that markup, the user hears twenty bare numbers. This is why this component turns up so often in accessibility discussions.
How it sounds at work
"This list is in cards — can we make it a table? We need to compare the values."
Asking for a layout that supports comparison over one that looks tidier.
"Twelve columns. Let's keep four and move the rest into the detail view."
Cutting columns before horizontal scrolling arrives.
"There's no sort arrow, so I can't tell what order this is in."
The state isn't displayed, so users can't confirm the result of their own action.
The longest conversation Noa and Theo had while fixing the admin screen was about which columns to keep. Watching how the operations team actually worked, the three things they searched on were name, storage and billing status; the other eight columns only mattered after opening one record. Cutting to four columns and moving the rest into the detail view removed the horizontal scroll. A pinned header row and sort indicators went in the same week, and the time it took operations to locate one account dropped noticeably.
What people get wrong
The most common reading is that this is the component for holding a lot of information. Rows and columns are there, so adding cells looks like adding capacity. But every added column narrows all the others, and at some point the view slides sideways and the identifying column leaves the screen. A fifteen-column table isn't an information-dense screen, it's a screen where nothing can be compared. In a 1,200-pixel layout, fifteen columns give each one 80 pixels, which isn't enough for a six-character team name. The test for keeping a column is whether anyone will sort or filter by it. If not, that value belongs in the detail view.
The other assumption is that cards are modern and tables are dated. A grid of boxes looks more considered on a new screen, and design galleries are full of them, so it reads that way. The two do different jobs. A box is good at presenting one record as a single unit; rows and columns are good at laying many records against one yardstick. Build a comparison screen out of boxes and the user has to do the comparing by eye, and past about thirty records they give up on it.
Where to go next
- UI component
the family this component belongs to
- Tabs
the layout component used alongside it to divide groups of columns
Last updated 2026-09-16