WhollySoftware
Back to blogWeb

Building a Dashboard With Heavy Data Tables That Stays Responsive

Wholly Software TeamApril 2, 20257 min read
Building a Dashboard With Heavy Data Tables That Stays Responsive

A logistics client's operations dashboard rendered shipment data in a table that worked fine in the demo with 200 rows and froze the browser tab entirely once real data hit around 8,000 rows. The table was rendering every row as a full React component tree with sorting and filtering computed on every keystroke against the full unfiltered dataset held in component state.

The first fix was virtualization — we swapped the plain table for TanStack Table paired with TanStack Virtual, rendering only the roughly 20-30 rows visible in the viewport at any time regardless of whether the dataset had 8,000 or 200,000 rows. This alone took initial render from a multi-second freeze to under 200ms, since the DOM only ever held a small, constant number of row elements.

Filtering and sorting needed to move off the main thread for the largest datasets. For anything under about 20,000 rows we kept filtering client-side but debounced it and used a Web Worker to run the filter logic so typing in the search box didn't block scrolling or other interaction. Above that threshold, we pushed filtering and sorting to the server with cursor-based pagination, since even worker-based client filtering started adding noticeable lag past that size.

Column resizing and cell editing were the other performance trap. Naive implementations re-render the entire table on every drag event during a resize or every keystroke during inline edit. We isolated resize state to a ref-based measurement that only committed to React state on mouse-up, and scoped cell edit state to the individual cell component rather than lifting it to the table root, which stopped full-table re-renders on every keypress.

For the export feature — a genuinely heavy operation, generating CSVs from 100,000+ rows — we moved generation server-side entirely rather than doing it in the browser, since client-side CSV generation on that volume was locking the tab for 10-15 seconds. The server job streams the file and emails a download link for anything over 10,000 rows, keeping the UI responsive regardless of export size.

The client's ops team went from avoiding the dashboard during peak shipping days, because it would lock up, to using it as their primary monitoring tool. Time-to-first-interaction on the table went from over 4 seconds to consistently under 300ms.

ReactData TablesPerformanceDashboards
Building a Dashboard With Heavy Data Tables That Stays Responsive — Wholly Software