Designing Data Tables: A Step-by-Step Guide for Product UI
Tables look easy to design. You put data in rows, add a header, and move on. Then real data arrives: long names, empty cells, 4,000 rows, a user on a small laptop. The table that looked clean in Figma starts to fall apart.
This is the process I'd follow when designing a table from scratch. It's a how-to, not a report from a project. The examples are hypothetical.
01Step 1: Write down what the table is for
Before drawing anything, finish this sentence: "People come to this table to ______."
Say it's a list of customer invoices. The answer might be "find an unpaid invoice and chase it." That's a different table from "check last month's totals." The first needs status and due date up front, with a quick way to filter. The second needs amounts, sorting and maybe a totals row.
If you can't pick one main job, pick the most frequent one and design for it. Secondary jobs can live in filters, menus or a detail view.
02Step 2: Choose and rank the columns
Start with every field you could show. Then sort them into three groups:
- Identity: what lets someone recognise the row (invoice number, customer name).
- Decision: what they need to choose an action (status, due date, amount).
- Reference: everything else (created by, internal notes, currency code).
Identity and decision columns go in the default view. Reference data goes into a detail panel or an optional column picker.
A useful test: hide one column at a time and ask whether the main job gets harder. If it doesn't, that column probably isn't earning its place.
Put the identifying column first
The first column is what people use to anchor their eyes as they scan across. Make it the thing that names the row, and if the table scrolls horizontally, pin it.
03Step 3: Align and format by data type
Alignment is one of the cheapest ways to make a table feel professional.
- Text: left-align, with the header left-aligned too.
- Numbers and currency: right-align, so digits stack by place value. Use tabular figures if your font offers them (in CSS,
font-variant-numeric: tabular-nums). - Dates: pick one format and stick to it. Consistent width helps scanning.
- Status: use a short label, not just a colour. Colour alone fails for people with colour vision differences, which is also covered by WCAG's guidance on not relying on colour alone to convey information.
- Actions: keep them in the same place on every row, usually the far right.
Decide how to handle long text up front. Truncating with an ellipsis keeps rows even, but make sure the full value is reachable by a tooltip, a detail view or a wrap-on-expand. Truncating something like an email address with no way to see the rest is a trap.
04Step 4: Set row density deliberately
Row height is a trade-off between how much people can see and how easy it is to hit a target.
I'd offer a sensible default and, if the audience is power users, a compact toggle. As a rough starting point, think in 8px steps (the same logic as in the invisible grid idea of consistent spacing, applied to rows). Something like 40px for compact and 48–56px for comfortable is a place to begin, then test with real content.
A few things help density without clutter:
- Use light horizontal dividers or subtle zebra striping, not both.
- Drop vertical gridlines unless columns are genuinely hard to separate.
- Keep cell padding consistent so the table has rhythm.
If the table sits inside a bigger screen, dashboard information density covers how to decide how much else should be competing with it.
05Step 5: Add sorting, filtering and selection with clear feedback
Each of these is a small feature that needs a visible state.
Sorting
- Make the sortable headers look clickable and show the current direction with an arrow.
- Decide on a default sort and show it. An unsorted table with no stated order feels random.
- Only one column sorted at a time is easier to understand than multi-sort. Add multi-sort only if people really need it.
Filtering
Show active filters as removable chips above the table, with a visible result count. If people can't tell the table is filtered, they'll think data is missing. I wrote about this failure mode in search and filter UX.
Selection and bulk actions
- Put checkboxes in the first column, and a "select all" in the header.
- When rows are selected, show a bar with the count and the available actions ("3 selected: Mark as paid, Delete").
- Be clear whether "select all" means this page or every matching row. If it means every row, say so with the number.
06Step 6: Design the states people forget
A table has more than one look. List these states and sketch each one:
- Loading: skeleton rows that match the real column layout, so nothing jumps when data lands.
- Empty (no data yet): explain what goes here and offer the action to add the first item.
- Empty (no results): say that filters returned nothing and offer "Clear filters."
- Error: say what failed and give a retry button. Keep existing data visible if you can.
- Partial or missing values: decide what an empty cell shows. A dash is usually better than a blank, because blank looks like a bug.
- Updating: if a row changes after an action, show it briefly so people notice.
Say a table has 25 rows per page and the user changes the sort. Do the rows blank out, fade, or stay put while new ones load? Pick one and apply it everywhere.
07Step 7: Plan for small screens and keyboards
On narrow screens a full table rarely fits. Two workable options:
- Cards: each row becomes a stacked card with the key fields first. This suits tables used for reading and tapping into items.
- Horizontal scroll with a pinned first column: this suits tables where people compare values across rows.
Pick based on the job from Step 1, not on what's quicker to build.
For keyboards and assistive tech, real table markup (<table>, <th>, <td>) gives you a lot for free, since screen readers understand it. If you build a table out of divs, you take on the work of recreating that. When using AI tools to generate the code, this is one thing I'd check; the designer's checklist for AI-built UI is where I'd start. Also make sure interactive elements in rows have visible focus styles and sensible labels. "Edit" repeated 25 times is vague, so something like "Edit invoice 1042" is better for screen reader users.
08A quick checklist
- The table's main job is written down.
- Columns are ranked, with reference data moved out of the default view.
- Numbers are right-aligned and formats are consistent.
- Row height is chosen on purpose and tested with real content.
- Sort and filter states are visible.
- Loading, empty, no-results and error states are all designed.
- Small-screen behaviour is decided.
- Semantic markup and focus styles are checked.
Most table problems I'd expect come from skipping step one and designing the happy path with ten tidy rows. Start from the job, rank what matters, and design the awkward states before they find you.
09Frequently asked questions
How many columns should a data table have?
There is no magic number. Start with the columns people need to identify a row and take the next action, then move everything else into a detail view, a column picker or an expandable row. If people scroll sideways to do their main task, you probably have too many.
Should numbers in a table be right-aligned?
Yes, in most cases. Right-aligning numbers lines up the digits so people can compare magnitudes at a glance, and it works best with a font that has tabular (fixed-width) figures. Text columns should stay left-aligned, with headers matching their column.
How do I make a table work on mobile?
Pick the approach based on the task. Stacked cards work when each row is a thing to read, while horizontal scrolling with a pinned first column works when people need to compare values. Either way, show the most important two or three fields first and put the rest behind a tap.
Denny Pratama
Product designer in Indonesia. Since 2018 I’ve designed SaaS products, dashboards and marketing sites, and now I build my own designs too.