Design a B2B data table by the job it serves: scan, find, act on one, act on many, return tomorrow. Density, filters, bulk actions and saved views. Design a data table around the job it serves, not around how it looks in a demo.
The short answer
Design a data table around the job it serves, not around how it looks in a demo. A working table in a B2B product has five jobs: scan and compare rows, find a record, act on one record, act on many, and let someone return tomorrow to the same view. Each job asks for different decisions on density, filters, selection and persistence.
Teams design the table for the demo: wide, spacious, a handful of tidy rows. People use it for the queue: filtered, dense, open all day. So the test we apply is simple. Can someone close the laptop, come back tomorrow and pick up where they left off? That needs saved views, stable column choices, and a selection that does not vanish on the next page.
This page covers the working grid. For charts and overview screens, see our guide to SaaS dashboard design.
The jobs, and the decisions each one forces
| Job | The question the table must answer | Decisions that matter | Common failure |
|---|---|---|---|
| Scan and compare | Which rows stand out? | Row height, column choice and order, alignment, frozen header and first column | Every column shown, none prioritised |
| Find a record | Where is the one I need? | Search versus filters, visible active filters, sorting | Filters that reset on refresh |
| Act on one | What can I do with this row? | Inline edit versus detail panel, row actions | Five icons per row, all equal |
| Act on many | Can I change fifty rows safely? | Selection scope, confirmation, undo | Selection lost on page two |
| Come back tomorrow | Is my view still here? | Saved views, a sensible default, persistence | Everyone starts from the same blank grid |
Most design advice on tables is a gallery of finished screens. A gallery shows what a table looks like once the decisions are made, not how to make them. The rest of this page takes the jobs in order.
Scan and compare
Density. Pick a default row height from the content, then let people change it. IBM's Carbon system offers five row heights, from 24 pixels to 64 pixels, with 40 as the default, and tells teams to match the header row to the chosen height and to reserve the tallest rows for cells that run to two lines (Carbon data table). GitHub's Primer offers three cell paddings: condensed, normal and spacious (Primer DataTable).
Our rule: default to the density the heaviest user needs, not the one that photographs best. Offer a toggle for the others, and remember the choice per person. For why one product can need two densities, see designing one interface for novice and expert users.
Density has a floor. WCAG 2.2 asks that pointer targets such as checkboxes and row buttons be at least 24 by 24 CSS pixels, or have enough spacing around them (success criterion 2.5.8, Target Size (Minimum), in WCAG 2.2). A compact row with a small checkbox can pass if the spacing is right, so check it rather than assume.
Columns. Choose the default columns by the decision the person makes in the row, and put the identifying column first. Keep related columns together. Carbon suggests one- or two-word titles, with longer ones wrapped and the full text in a tooltip. Everything else goes behind a column picker.
Alignment. Left-align text and right-align numbers, so digits line up for comparison. Primer gives both rules; GOV.UK gives the second, for columns of numbers you need to compare.
Frozen columns. When the table is wider than the screen, freeze the header row and the identifying column so the reader never loses what a cell means. Keep the frozen set small, or it eats the viewport.
Zebra stripes and hover. Carbon treats stripes as optional for horizontal scanning and keeps row hover on at all times. We found no primary study on stripes that we could verify, so treat them as a choice to test with your own users, not a rule.
Find a record
Search, filters and sorting solve different problems, and mixing them is the commonest source of confusion.
- Search is for when you know something about the record, such as a name or an ID. It should be one box, always visible, and forgiving.
- Filters are for narrowing by attributes you can name: status, owner, date range, plan. Use the product's own words for them.
- Sorting is for ordering whatever is left.
- A saved view is for a combination of all of these that someone needs again.
A rule of thumb we use: if people type it, it is search; if they pick it, it is a filter; if they keep re-creating it, it is a saved view.
Whatever you choose, make the state visible. Active filters should show as removable chips or a count above the table, with one control to clear all of them. GitLab's Pajamas guidance puts filter controls directly above the table with a way to clear them (Pajamas table). Keep complex filters in the toolbar; Carbon reserves the toolbar for table-wide actions and limits it to five before overflowing.
Sort the way people expect: first click sorts ascending, a second click reverses it, and only the sorted column shows its arrow. Tell assistive technology which column is sorted with aria-sort, as the WAI-ARIA table pattern describes.
Pagination or infinite scroll. Pajamas paginates anything over 20 items, and over 40 for condensed tables. For work tables we lean to pagination or a "load more" control, because people need to know where they are, return to a place, and select within a known set. Infinite scroll suits feeds, not queues.
Act on one record
Decide how much happens in the row and how much in a detail view.
- Inline edit fits short, low-risk fields where people change many rows in a row: a status, an owner, a date. It keeps the context.
- A side panel or detail page fits anything with several fields, validation, or consequences. Carbon says the same about expansion: if expanded content feels cramped, move it to a side panel or its own page. It also notes that an expand-all control can undo the benefit of deferring data loading.
Row actions need a hierarchy. Primer puts a single action in the last column, groups several in a menu, and pulls out at most one heavily used action. A row with five equal icons asks people to read every one on every row.
If creating a record starts from the table, search before creating, as in this search-first create flow.
Act on many
Bulk actions are where a table stops being a report and becomes a tool, and where mistakes get expensive. The common pattern is a checkbox in each row, a header checkbox with checked, unchecked and indeterminate states, and an action bar that appears once something is selected. Carbon uses this, and disables single-row actions and row menus while the bar is active, so people cannot mix the two.
A bulk-action safety checklist we use:
- Say what is selected. Show a count in the action bar ("12 selected"). If a filter is active, say so.
- Make the scope explicit. After "select all", state whether it means this page or every row matching the filter, and offer the wider choice separately, with the total. Never let the two look the same.
- Keep the selection across pages. If it resets on the next page or after a sort, people will act on fewer rows than they think, or give up.
- Match the confirmation to the damage. Reversible changes need none. Destructive or hard-to-reverse ones need a confirmation naming the count and the action, not a generic "Are you sure?".
- Offer undo or a way back. Prefer an undo for a short time after the action over a confirmation box before it. Where undo is impossible, say so in the confirmation.
- Report the result. If forty of fifty rows changed, say which ten did not and why.
- Make it keyboard-reachable. The action bar must be in the focus order and not hidden behind a sticky element.
Items 2 to 6 are our practice, not a published standard, so test them with your own data volumes.
Come back tomorrow: saved views
This is the job most tables skip, and for a queue-driven product it matters most. A saved view stores a configuration someone needs again. The Basis Design System lists what a view should hold: sort field and direction, column widths, custom columns and filters (Basis saved views).
Its other points are worth taking as defaults:
- Ship at least one sensible default view, based on how people use the table most.
- Let people edit a view and either save the change to it or save it as a new one.
- Keep the selected view when they leave the page or close the browser.
- Skip saved views for simple tables with few settings; they add interface for no gain.
From our own practice, add three more. Give a view a clear name and show the current one at all times. Mark a view as changed when its filters have been adjusted but not saved. And let people share a view by link, since a queue is a team's shared language.
Saved views are not a substitute for good defaults. They are what makes a good default survive contact with different roles.
When a table is the wrong answer
GitLab's guidance is a useful boundary. Use a table to compare entries that share the same attributes, or to let people review, edit or filter granular data. Do not use one for page layout, for a simple vertical list, for content and actions about one topic (use a card), or for hierarchical data (use a tree).
We would add one more case: work that moves through stages can be better as a board, because the stage is the thing people are managing. If people mostly drag items between states, a table of the same rows is the wrong view. Keep the table as an alternative view of the same data; this is the idea behind object-oriented UX.
If you are choosing a front-end grid library, that decision matters less than the rules above: every serious library can do sort, filter and selection. The hard part is deciding what they should do.
States you must design, and keyboard use
States. A table has more states than the populated one:
- Loading. Use skeleton rows that match the row height rather than a spinner; Carbon recommends skeletons. Keep the header and toolbar in place.
- Empty, first use. Say what will appear here and how to create it.
- Empty, filtered. Say that filters returned nothing, show which are active, and offer to clear them. This is a different message from first use.
- Error. Say what failed, keep the filters and selection, and offer a retry.
- Partial or missing values. Pajamas prefers descriptive text such as "Unassigned" to an empty cell.
- Selected, edited, saving. Show a row's state while it changes, so people know whether their edit landed.
Keyboard and assistive technology. Start with the right pattern. The WAI-ARIA Authoring Practices describe a table as static, where every focusable element in it is a tab stop, and a grid as a composite widget with one tab stop and arrow-key movement between cells. Use a grid when cells are interactive or editable (APG grid pattern). In a grid, arrow keys move one cell, Home and End move along the row, Page Up and Page Down move by rows, and Enter or F2 enters a cell to edit, with Escape returning to navigation.
Whatever you choose:
- Every function must work from the keyboard (WCAG 2.1.1).
- A sticky header or action bar must not completely cover the focused element (WCAG 2.4.11).
- Give each table a caption and mark headers with
scope, as GOV.UK and Pajamas both advise. - Wide data tables are exempt from the reflow requirement, so horizontal scrolling is acceptable, but individual cells must still reflow (WCAG 1.4.10).
Where to go from here
If the main work of your product happens in a grid, treat it as the product's centre, not a component. Our work on data visualisation and data-rich interfaces covers this, and so does our data analytics and decision products expertise.
About the authors
Rishabh Choudhary
Product Designer
More from RishabhRishabh Choudhary is a Product Designer at Tcules who writes about design systems and about designing for people the average interface leaves out. Rishabh's articles look at ten age-inclusive products, inclusive design for older and disabled people, eLearning platforms from the side of both learners and administrators, and software for lab and life-science teams. On design systems, the pieces cover components that several brands can reuse and B2B SaaS design systems worth studying. Rishabh has also co-written on fintech UX, healthcare UX and choosing a fintech design agency.
Yash Gidwani
Product Designer
More from YashYash Gidwani is a Product Designer at Tcules who writes about getting people into a SaaS product and keeping them there. Yash's guides cover SaaS onboarding strategies, onboarding and engagement software, and redesigning a live product without upsetting the people who use it. Other articles go into specific workflows: lending and loan origination apps, insurance quoting and claims, staffing and recruitment software, and a search-first create flow that stops duplicate records. Yash has also co-written the SaaS UX audit checklist and a 20-question checklist for legacy modernisation readiness.