zynactradocs

Server data table

Search, filter and page through records the server holds, with debounced requests, cancellation, skeletons and "Load more".

The data table pattern filters rows it already has. When the server holds more than you want to load at once, the server searches and pages instead. This is the markup and behaviour to copy, as components/<thing>-table.tsx.

PersonRoleScopeStatus
0 shown

How it's built

PartBuilt with
SearchInputGroup; the query is debounced (250 ms) before it's sent, and a Spinner shows in the field while a new search runs over old rows
FiltersToggle variant="outline" for on/off filters ("Show revoked"); a Select for a choice of several
CancellingEvery request gets an AbortController; a new query aborts the one in flight, so old answers never overwrite new ones
First loadSkeleton rows the same height as real ones
RefreshingThe old rows stay, dimmed, until the new page arrives; aria-busy on the table
PagingCursor paging: the server returns nextCursor, "Load more" appends the next page. The count says how many are shown, not a total the server didn't compute
ErrorsAlert destructive above the table, saying what to do next; rows already loaded stay
PeoplePerson in cells, never raw IDs
No resultsEmpty across all columns, quoting the query

Cursor or page numbers

  • Cursor ("Load more") when rows are added often (logs, assignments) or counting is expensive. Pages never shift under people, and the server stays fast.
  • Page numbers (Previous and Next with "Page 2 of 9") when people need to jump or share a position. That needs a total count from the server.

Either way, keep the query, filters and position in the URL (?q=kasun&revoked=1), so the back button and a shared link restore the view.

Guidelines

  • Never filter server data on the client as well; people will think the server found nothing.
  • Don't clear rows to show a spinner on each keystroke. Keep them and dim them.
  • Reset to the first page whenever the query or a filter changes.
  • Everything from the data table guidelines applies: no brand in the table, a border not a card, five to seven columns.
Edit on GitHub

On this page