QuestionsReact

Diagnose & fix a slow list

PerformanceHardReact

A list of 10,000 rows scrolls badly and typing in a filter lags. How do you diagnose and fix it?

What it tests

Real performance methodology: measure first, then apply the right fix (virtualization, memo, keys).

Approach & answer

Measure first with the React Profiler and browser performance tab — don't guess. Typical fixes, in order of impact: (1) virtualize — render only the visible window (react-window/react-virtualized) so the DOM holds ~20 nodes not 10,000; (2) stable keys — use real ids, never the array index, or React remounts rows on reorder; (3) memoize rows with React.memo and stabilize the row callbacks/data with useCallback/useMemo so unchanged rows skip re-render; (4) debounce the filter input; (5) keep expensive derived data (sorting/filtering) in useMemo. Virtualization is almost always the big win for large lists. The methodology matters as much as the fixes: use the Profiler's flame chart to see WHICH components rendered and why (it flags 'why did this render'), and the browser Performance panel to separate a scripting bottleneck (too many renders / expensive render) from a layout/paint bottleneck (too many DOM nodes, forced reflow). That diagnosis picks the fix — if the DOM is huge, virtualize; if renders are frequent, memoize and stabilize identities; if a single render is slow, move work out of render or into useMemo. Watch for interaction cost too: with 10k rows even the filter's setState can jank, so debounce input and consider useDeferredValue/useTransition to keep typing responsive while the list updates at lower priority. Always re-measure after each change so you're optimizing the real bottleneck, not a guessed one.

Use this technique when

Large tables/feeds, data-grid components, anything rendering thousands of nodes — common in BI dashboards.

Code

import { FixedSizeList } from 'react-window';

const Row = React.memo(({ index, style, data }) => (
  <div style={style}>{data[index].label}</div>
));

function BigList({ rows }) {          // rows.length === 10000
  return (
    <FixedSizeList height={400} itemCount={rows.length}
      itemSize={32} width="100%" itemData={rows}>
      {Row}
    </FixedSizeList>   // only ~visible rows are in the DOM
  );
}

References