JM Family Design System
Patterns

Data Table

The structure for scanning, comparing, sorting, filtering, selecting, and acting on records that share the same attributes. A data table gives users a stable grid, clear controls, and honest states for loading, empty, filtered, and paginated data.

When to use it

Use the data table when the record set itself is the work: scanning, comparing, sorting, filtering, selecting, and acting on rows.

Use a data table when

  • Rows share the same attributesUse a table when each record can be compared across stable columns such as status, owner, date, amount, or location.
  • Users need to scan or compareThe main job is finding differences, spotting outliers, checking status, or choosing which records need attention.
  • Controls change the result setSorting, filtering, search, pagination, saved views, or bulk selection are part of the task.

Use something else when

  • The content is not comparableUse cards, lists, or detail pages when each item has different attributes or depends on imagery, long text, or narrative context.
  • The user is editing one objectUse a form layout or detail page when the task is to create, review, or edit one record at a time.
  • There are too few attributes to justify a gridUse a simple list when users only need a name, one supporting line, and one action.

Anatomy

The table owns the relationship between controls, result state, headers, rows, and actions. Individual primitives still own their control behavior.

  • Table title or caption

    Required

    Name the record set in plain language. The title or caption tells assistive technology users what the table contains.

    Use a visible heading near the table and a caption or aria-labelledby connection for the table element.

  • Toolbar

    Conditional

    Search, filters, saved views, export, and create actions live above the table when they change or act on the result set.

    Keep active filters visible. Do not hide the reason a table changed.

  • Column headers

    Required

    Headers are short nouns or noun phrases. Sortable headers expose both the action and current sort state.

    Use th scope="col" and aria-sort on the sorted column.

  • Rows and cells

    Required

    Cells stay aligned to their headers. Status cells include readable text, not color alone.

    Use semantic table markup for comparable data; avoid div grids for ordinary tables.

  • Selection and row actions

    Conditional

    Selectable tables show a labeled checkbox per row, a selected count, and actions that apply to the selection.

    Do not make the entire row the only interactive target when individual links, checkboxes, or actions are present.

  • Pagination and result count

    Conditional

    Large result sets show how many records exist, what page is visible, and how users move through pages.

    Keep pagination controls keyboard reachable and preserve filters while changing pages.

Variants

Choose the table variant by the job: compare rows, select work, or recover from an empty filtered result.

Record comparison table

A table optimized for scanning records across stable columns.

Trigger: Users need to compare many records by status, owner, date, amount, or other repeated attributes.

Job: Keep columns predictable, make important status visible in text, and preserve row-to-column alignment.

Selectable work queue

A table for reviewing multiple records and applying a shared action.

Trigger: Users need to select one or more rows before assigning, approving, exporting, or archiving records.

Job: Make selected state, selected count, and available bulk actions explicit without hiding row-level actions.

Filtered empty table

A table state that preserves controls when filters return no rows.

Trigger: Search, filters, or saved views produce no matching records.

Job: Explain the empty result, keep active filters visible, and give a direct recovery action.

Examples

The previews show table controls, row states, active filters, and empty-state recovery in realistic product context.

Vendor compliance

Vendor compliance

Sort by renewal date and scan status before opening a vendor.

Vendor compliance

42 vendors

StatusAllOwnerAll
Sort by renewal date and scan status before opening a vendor.
Owner
Acme Services LLCNeeds reviewM. CarterMay 28, 2026
Northstar SupplyApprovedR. SinghJun 14, 2026
Urban Fleet GroupExpiring soonA. GomezJun 21, 2026
Page 1 of 510 per page

Access requests

Access requests

Selected rows expose bulk actions while each row remains understandable on its own.

Access requests

18 requests

1 request selectedBulk actions apply only to checked rows.
Selected rows expose bulk actions while each row remains understandable on its own.
StatusOwner
Finance dashboard accessPendingTodayUnassigned
Vendor admin roleNeeds reviewYesterdayJ. Lee
Reporting export permissionPendingMay 18, 2026Unassigned

Vendor compliance

Vendor compliance

The table is empty because the current filters exclude every row.

Vendor compliance

0 vendors

StatusExpiredOwnerUnassigned
The table is empty because the current filters exclude every row.
Owner

No vendors match these filters

Clear filters or adjust Status and Owner to broaden the result set.

Filtered no-results state

Wrong

No data

The table body disappears and the active filters are hidden.

Right

No vendors match these filters

Status: Expired and Owner: Unassigned remain visible with a clear recovery action.

Clear filters

The user needs to understand why the table is empty and how to recover without rebuilding their search from memory.

Sortable column

Wrong

Renewal

A small arrow changes direction, but the header has no button semantics or aria-sort state.

Right

Renewal date

The header is keyboard accessible and exposes aria-sort="ascending".

Sort by renewal date

Sort is an interaction and a state. Both need to be visible and available to assistive technology.

Bulk action table

Wrong

Selected rows

Rows change background color, but there is no selected count and the action button only says "Apply."

Right

1 request selected

The toolbar names the selection and exposes specific actions such as "Approve selected" and "Assign."

Approve selected

Bulk action tables need explicit state. Users should never infer selection or action scope from color alone.

Voice and content

Table copy should support scanning. Labels need to be stable, short, and specific enough to explain the data.

  • Use noun-first column labels

    Column headers should name the attribute: "Renewal date," "Owner," "Status." Avoid instructions such as "Click to renew."

  • Put units and formats where users compare

    If a column needs units, date format, or currency, make that clear in the header or cell formatting.

  • Write status as text

    Badges and icons can help scanning, but the cell still needs words such as "Approved," "Pending," or "Needs review."

  • Name table actions by outcome

    "Approve selected" and "Export vendors" beat vague labels such as "Apply" or "Go."

Accessibility

Table accessibility depends on semantic markup, labeled controls, visible state, and predictable focus when the result set changes.

  • Use semantic table structure

    Use table, caption, thead, tbody, th, and td for comparable data so headers and cells stay connected.

  • Expose sort state

    Set aria-sort on the currently sorted header and make sortable headers keyboard accessible.

  • Label selection controls

    Header and row checkboxes need specific accessible labels such as "Select all vendors" and "Select Acme Services LLC."

  • Do not rely on color alone

    Status, selected state, errors, and warnings need text or icons in addition to color.

  • Preserve focus through table state changes

    Filtering, pagination, and bulk actions should keep focus predictable and announce important result-count changes.

Implementation notes

The table pattern coordinates state, controls, and semantic structure. It should not become a collection of unrelated divs and buttons.

  • Compose controls from primitives

    Use Button, Checkbox, Link, Badge, Tooltip, Text Input, and Select before adding custom table controls.

    Primitives: Button, Checkbox, Link, Badge, Tooltip, Text Input, Select

  • Keep table state explicit

    Represent sort, filter, search, page, and selection state in code and, when useful, in the URL so refresh and sharing preserve the same view.

  • Reuse the empty-state pattern

    No-data and no-results states should follow the Empty State pattern. Filtered empties explain which controls caused the result.

    Primitives: Button

  • Protect comparison on small screens

    Prefer horizontal scrolling with clear headers for dense comparison. Only transform rows into cards when the comparison job still works.

Review checklist

Use this as the acceptance gate before approving a data table.

  • The table has a title or caption that names the record set.
  • Column headers are semantic, short, and connected to their cells.
  • Sortable columns expose current sort state with aria-sort.
  • Filters, search, result count, and pagination remain visible when they affect the result set.
  • Selection controls are labeled and bulk actions show selected count.
  • Empty and filtered-empty states explain what happened and how to recover.
  • Status and selected states do not rely on color alone.