All posts
Product design

Data Table Design for Web Apps: Sorting, Filtering, Bulk Actions and Density

Data table design for web apps: sorting, filtering, bulk actions, density modes, empty vs zero-results states, and how to spec it so it builds in TanStack Table.

By Mehdi Hassan8 min read

The short answer

Good data table design makes rows easy to scan, compare and act on. Align numbers right and text left, keep headers sticky, make sorting and filtering visible, support bulk actions through row selection, offer a compact density option, and design empty, zero-results, loading and error states separately. Spec it in terms your React table library can build.

Key takeaways

  • Tables are for lookup and action; charts are for patterns. Design tables around the task people come to do.
  • Right-align numbers, left-align text, and use tabular figures so columns compare cleanly.
  • An empty table (no data yet) and zero results (filters too narrow) need different messages and actions.
  • Offer two or three density modes rather than picking one row height for everyone.
  • Write specs in table-library terms (column defs, sorting, selection) so design and build match.

Part of Dashboard Design: The Complete Guide to Dashboards People Actually Use

What makes good data table design?

Good data table design lets people find a row, compare values and act on what they find, quickly and without errors. Tables are where most work in a web app actually happens: reviewing accounts, approving requests, triaging tickets. A table that's slow to scan or awkward to filter costs users time every single day.

Within a dashboard, tables usually sit below the KPIs and charts and answer "which ones?" Our dashboard design guide covers that wider layout. This article goes deep on the table itself, and on something most table guides skip: how the design gets built in React, so the spec your designer writes is the one your developer ships.

Principle

In practice

Scannable

Clear alignment, restrained borders, readable row height

Comparable

Right-aligned numbers, tabular figures, consistent units

Findable

Visible search, filters and sorting

Actionable

Row actions, row selection and bulk actions

Honest about state

Distinct empty, zero-results, loading and error states

How should you lay out columns and rows in table UI design?

Lay out columns in order of importance from left to right, starting with the column that identifies the row, such as a name or ID. In table UI design, alignment does most of the work: left-align text, right-align numbers so decimal places line up, and align each header with its column's content.

  • First column identifies the row. Usually a name with a secondary line, and it stays visible when the table scrolls sideways.
  • Right-align numbers and use tabular figures. Put units in the header ("Revenue, USD") rather than repeating them in every cell.
  • Keep headers sticky on long tables so people never lose track of which column is which.
  • Use restraint with lines. Horizontal row dividers or subtle zebra striping help; full grid borders add noise.
  • Truncate with care. Truncate long text with an ellipsis and show the full value on hover or in the detail view, never in a way that hides the distinguishing part.
  • Let people choose columns. Column visibility, and on wide tables reordering, helps different roles see what they need.

What are table UX best practices for sorting and filtering?

The core table UX best practices for sorting and filtering are to make the current sort and active filters always visible, keep them easy to change or clear, and pick sensible defaults. Users should never wonder why a row is missing or why the order looks wrong.

  • Sorting: click a header to sort, show an arrow on the sorted column, and default to the order that matches the task, such as most recent first or most urgent first.
  • Filtering: show common filters inline above the table, and put rarer ones in a filter panel. Show each active filter as a removable chip with a "Clear all" option.
  • Search: one search box that searches the columns people expect, with a note on what's searched if it isn't obvious.
  • Persistence: remember a user's sort, filters and columns between visits, and reflect them in the URL so a filtered view can be shared.
  • Counts: show the result count ("48 of 1,240 accounts") so people know how much filters have narrowed the list.

Filter design gets complicated fast once you add facets, saved views and combined conditions. Our guide to search and filter UX covers faceted search in detail.

How do you design bulk actions UX?

Design bulk actions UX around row selection: a checkbox per row, a select-all in the header, and an action bar that appears only once something is selected. The action bar shows how many rows are selected and the actions available for them, which keeps the default view clean and makes the selection state obvious.

  1. Select

    Checkboxes on each row and in the header. Shift-click to select a range is a welcome extra for power users.

  2. Clarify the scope

    When select-all is used on a paginated table, ask whether to select this page or all matching rows, and show the count.

  3. Show the action bar

    A bar with "12 selected", the available actions and a clear way to deselect.

  4. Confirm destructive actions

    Deleting or changing many records needs a confirmation that names the count and the effect.

  5. Report the result

    Say what happened, including partial failures ("10 updated, 2 couldn't be changed"), and offer undo where possible.

Bulk actions matter even more when some actions are taken by AI, such as suggested updates waiting for approval. Our guide to human-in-the-loop UX covers review queues and undo for those cases.

Which density modes should a data grid offer?

A data grid should usually offer two or three density modes, such as comfortable, default and compact, because different users and tasks need different row heights. Someone reviewing ten accounts a day wants room to read; someone reconciling hundreds of transactions wants as many rows on screen as possible.

Density

Best for

Trade-off

Comfortable

Occasional users, rich rows with avatars or two lines

Fewer rows per screen

Default

Most users and most tasks

A balance of scanning and reading

Compact

Power users, finance and ops tables, large screens

Harder to click on touch, denser to read

Density should change spacing and row height, not font size. Shrinking text to fit more rows makes everything harder to read. Store the density choice per user, and make sure compact rows still have large enough click targets for checkboxes and row actions. Our product design glossary explains density and the related terms designers use.

What's the difference between an empty state and zero results?

An empty state means there's no data yet, while zero results means data exists but the current search or filters exclude all of it. They look identical if you only show an empty table, but they need opposite responses: the first helps someone create data, the second helps them loosen their filters.

State

Cause

Message

Primary action

Empty (first use)

Nothing created or imported yet

Explain what will appear here

Create, import or connect

Zero results

Filters or search too narrow

"No accounts match these filters"

Clear filters, edit search

Cleared

User finished all items, e.g. an inbox

A short confirmation

None, or go elsewhere

No permission

User can't see these records

Say access is restricted

Request access

Error

The query failed

Plain explanation

Retry

How do you spec a data table so it builds in React with TanStack Table?

Spec a data table in the same structure your developers will use to build it. With TanStack Table, a popular headless library for React, that means column definitions, sorting, filtering, pagination, row selection and column visibility. TanStack Table handles the table logic and state but no visuals, so your design system supplies the markup and styles.

  • Column definitions: for each column, the header label, the data field, alignment, cell format (currency, date, badge), whether it sorts, whether it filters and its default visibility.
  • Sorting and filtering: say whether they run in the browser or on the server. Large tables usually need server-side sorting, filtering and pagination, which the library supports through manual modes.
  • Row selection: which rows can be selected, how select-all behaves across pages, and which bulk actions are available.
  • Pagination or virtualization: paginate when people need to reference positions; virtualize long scrolling lists so only visible rows render.
  • States: loading skeleton rows, empty, zero-results, error and partial failure, as specified above.
  • Responsive behavior: which columns hide first, whether the first column pins, and when the table becomes a list on small screens.
Hypothetical example: a column spec for an invoices table

For example, suppose a billing product has an invoices table. A buildable spec might read:

Customer: text, left-aligned, sortable, searchable, pinned on horizontal scroll, always visible. Amount: currency, right-aligned, tabular figures, sortable, filter by range. Status: badge with label (Paid, Due, Overdue), filter by multi-select, default filter excludes Paid. Due date: date, sortable, default sort ascending. Actions: menu with View, Send reminder, Mark as paid; hidden from read-only roles.

Bulk actions: Send reminders and Export, available when one or more rows are selected. Server-side sorting and pagination, 50 rows per page, sort and filters stored in the URL. That's a spec a developer can map directly to column definitions without guessing.

When one team owns both design and code, the spec and the build come from the same decisions, which is the point of working this way. For tables inside larger internal tools, see our guide to admin panel design, and run your table against the SaaS dashboard checklist.

Frequently asked questions

Should data tables use pagination or infinite scroll?
Use pagination when people need to find their place again, compare pages or select across a known set, which covers most business tables. Infinite scroll suits feeds where order matters less. For very long tables, virtualization renders only visible rows and keeps scrolling fast. Whatever you choose, show the total count and keep sort and filters stable as people move.
How do data tables work on mobile?
Wide tables rarely work as-is on phones. Common approaches are pinning the first column and letting the rest scroll sideways, hiding lower-priority columns, or turning each row into a card that shows the key fields with a tap for details. Decide which two or three fields matter most per row before designing the small-screen version.
Should table rows be clickable?
Making the whole row clickable to open a detail view is common and efficient, but it conflicts with checkboxes, links and inline actions inside the row. If you do it, keep those controls clearly separate, show a hover state, and make sure keyboard users can open the row. If rows aren't clickable, make the name in the first column a link.
What's the difference between a data table and a data grid?
The terms overlap. A data table usually means a read-mostly table with sorting, filtering and row actions. A data grid usually adds spreadsheet-like features such as inline editing, cell selection, column resizing and keyboard navigation between cells. Most SaaS products need a data table; finance, ops and admin tools sometimes need a full grid.

· CEO & Design Director

Copywriter, designer and developer, and founder of Designify Studio. Over five years, 60+ projects across 14+ industries, for startups that have raised $300M+.

Want this done for you? Brand & design sprint, $3,000.

A brand refresh or redesign, plus design explorations for your website and product. $3,000, one-time, fixed price. Book a free 30-minute call to see if it fits.

Schedule Call