Pattern 01
Rendering the whole list
What we see
A table or feed that maps directly over the response array. With the mocked twenty rows it is instant. With the real response — a few thousand rows, each a component with its own subscriptions and event handlers — the main thread blocks for seconds on every render, and interaction latency goes with it.
Why an assistant produces it
array.map is the idiomatic React list, and it is correct. Virtualisation requires knowing the row count will be large, which the mock data actively contradicted.
The pattern
{rows.map((r) => <Row key={r.id} row={r} />)}The fix
// Render the window, not the dataset.
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 44,
overscan: 12,
});
{virtualizer.getVirtualItems().map((v) => (
<Row key={rows[v.index].id} row={rows[v.index]} style={offsetFor(v)} />
))}The correct implementation
Any list whose length is controlled by the server rather than by you needs either virtualisation or server-side pagination — decided when the component is written, because retrofitting it means rewriting the scroll container, the selection model, and any sticky headers.
How to test it
Point the component at a fixture with 10,000 rows and record a performance profile while scrolling. Look at long tasks and dropped frames, not at whether it eventually renders. Then repeat with CPU throttled 4x — that is closer to the median user's device than your laptop is.