fbpx

+48 608 607 850
+48 22 490 35 28

info@diatta.pl

React List Virtualization: Setup, Examples & Performance Tips





React List Virtualization: Setup, Examples & Performance Tips





React List Virtualization: Setup, Examples & Performance Tips

Short summary: rendering thousands of DOM nodes in React is a fast track to UI lag. Virtualization (windowing) keeps DOM size small and performance predictable. Below — practical guidance, quick setup, and advanced tips.

Further reading: advanced list virtualization with react-list, React list virtualization (react-window), React virtualized list.

Why virtualize lists (and when to avoid it)

Rendering every item in a large collection creates a proportional DOM size. The browser pays for each node: layout, paint, memory, and event hooks. When the number of rendered items grows past a few hundred, user-perceived performance often degrades — janky scrolling, delayed interactions, and long initial loads. Virtualization (also called windowing) solves this by keeping the DOM small: only items visible in the viewport plus a small overscan are mounted.

That said, virtualization has trade-offs. It complicates layout (especially with dynamic heights), interferes with some CSS features (e.g., natural focus order or CSS sticky if not handled carefully), and can hurt a11y if not implemented with attention. If your list is short (tens of items), or rows must participate in complex layout interactions, virtualization may be premature optimization.

Rule of thumb: virtualize when you see measurable pain — long initial paint, scrolling jank, or memory pressure — or when the dataset is large by design (chat history, logs, feed, large tables). Otherwise, keep it simple and save the mental overhead for real problems.

Common user intents and the competitive landscape

For the keywords you provided (react-list, react-list tutorial, React virtualized list, etc.), user intent clusters into: informational (how virtualization works, pros/cons), transactional/navigational (install, setup guides, library docs), and commercial (comparing libs, paid components or enterprise solutions). Many top-ranking pages combine API reference + examples + benchmarks: README + live demos + „getting started” sections.

Competitors typically structure content as: quick overview, install/usage, minimal example, advanced cases (variable height, infinite scroll, grouping), and performance knobs (overscan, itemKey, memoization). Articles that rank high include clear code examples, runnable sandboxes, and practical benchmarking notes.

To outrank such pages, an article should be concise, include copy-paste-ready examples, cover variable-height edge cases, show infinite scroll integration, and offer small performance checklist items. That’s what this guide focuses on.

Which libraries to consider (and why)

There are three widely used approaches/libraries: lightweight modern windowing (react-window), mature feature-rich (react-virtualized), and niche/single-purpose packages (react-list and others). React-window focuses on speed and a small surface area, reacting to fixed or variable sizes. React-virtualized provides many higher-level utilities (Grid, CellMeasurer, AutoSizer, InfiniteLoader).

If you need minimal footprint and predictable behavior, start with react-window: simple APIs and great perf. If you need complex measurements, dynamic heights, or built-in helpers, react-virtualized still has value. The advanced list virtualization with react-list article explores some specialized techniques for variable heights and recycling strategies.

Choosing a library depends on your constraints: bundle size, need for variable heights, virtualization for grids/tables, or pre-built utilities. Often the best choice is to prototype with react-window and add reactors (ResizeObserver / measurement) only if necessary.

Installation & getting started

Install the most-used libraries quickly. Use the one that matches your needs:

npm install react-window
# or for legacy/feature-rich support
npm install react-virtualized
# if you specifically want react-list
npm install react-list

Minimal fixed-height example with react-window (copy-paste friendly):

import { FixedSizeList as List } from 'react-window';

function Row({ index, style }) {
  return <div style={style}>Row {index}</div>;
}

export default function App() {
  return (
    <List
      height={600}
      itemCount={10000}
      itemSize={35}
      width={'100%'}
    >
      {Row}
    </List>
  );
}

This renders 10,000 logical rows but only mounts a handful visible at once. Adjust itemSize for fixed rows; use VariableSizeList if heights differ.

Handling variable-height items

Variable heights are the notorious edge case. The simplest options: (1) measure and cache heights ahead of virtualization (if possible), (2) use a library that supports dynamic measurement (react-virtualized’s CellMeasurer), or (3) use react-window with a custom SizeMeasurer and ResizeObserver to update the cache when items mount or change.

Common pattern: render items with a short placeholder height until measured, then update the size cache and re-render the window. The measurement can be done via ResizeObserver or by reading getBoundingClientRect during an effect. Remember to debounce updates and avoid forcing layout too often.

If your content reflows (images load, fonts swap), ensure the measurement system updates sizes. Otherwise scrolling will jump when heights change — the UX equivalent of stepping on a LEGO brick.

Performance checklist & best practices

Practical optimizations that reduce jank and CPU cost:

  • Memoize row renderers (React.memo) and avoid inline functions where possible.
  • Use itemKey or keyExtractor prop to keep identity stable.
  • Tune overscan to balance perceived smoothness vs DOM size.
  • Lazy-load images and heavy subcomponents inside rows.
  • Batch measurements and use ResizeObserver for dynamic content.

Additionally, avoid excessive event listeners on each row. Delegate where possible. When using virtualization inside flex layouts, fix the container height or use AutoSizer to compute it automatically.

Finally, benchmark: use Chrome DevTools performance profiler, record a scroll, and look for long tasks and layout thrashing. Optimize the top offenders — a targeted change usually returns the best ROI.

Example: infinite scroll + virtualization

Combine an infinite loader with a virtualized list to stream data on demand. The pattern: (1) the virtual list asks for rows in the viewport, (2) when rows near the end are requested, trigger a fetch, (3) append data and let the list render new rows. Libraries like react-window-infinite-loader and react-virtualized InfiniteLoader implement this.

import { VariableSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';

// pseudo example
const isItemLoaded = index => !!items[index];
const loadMoreItems = (start, stop) => fetchMore(start, stop);

<InfiniteLoader
  isItemLoaded={isItemLoaded}
  itemCount={totalCount}
  loadMoreItems={loadMoreItems}
>
  {({ onItemsRendered, ref }) => (
    <List
      ref={ref}
      onItemsRendered={onItemsRendered}
      height={600}
      itemCount={items.length}
      itemSize={index => getCachedSize(index) || 50}
      width={'100%'}
    >
      {Row}
    </List>
  )}
</InfiniteLoader>

Key points: guard against double-fetches, show placeholders for loading rows, and keep totalCount accurate if the backend provides it. Use a small overscan to prefetch slightly ahead of scroll.

If you’re using server-side rendering, render a static snapshot or a small initial window and hydrate the rest on client — avoid trying to virtualize SSR full datasets unless necessary.

Advanced topics and pitfalls

Sticky headers, grouped lists, and virtualization: you can virtualize grouped lists by rendering group headers as part of the item stream and adjusting indices. For true sticky behavior across virtualized windows, you may need a separate fixed-position header that syncs with the first rendered group index.

Keyboard accessibility and focus: virtualized lists can unmount focused items, breaking tab order. Keep one of two strategies: (1) keep focused items mounted (pin them), or (2) maintain a logical focus index and re-focus programmatically when items remount. Always test with keyboard navigation and screen readers.

Testing: visual regression tests and end-to-end tests should account for virtualization. Some test runners may not scroll to create renders; make sure to simulate scroll or explicitly mount the list with a desired scroll offset in tests.

Three short, actionable examples (copy-paste)

1) Quick fixed list (react-window): already shown above. Use this for thousands of small rows.

2) Variable list with ResizeObserver sketch:

// inside Row component
useEffect(() => {
  const ro = new ResizeObserver(entries => {
    for (let e of entries) {
      const height = e.contentRect.height;
      sizeCache.set(index, height);
      listRef.current.resetAfterIndex(index);
    }
  });
  ro.observe(nodeRef.current);
  return () => ro.disconnect();
}, [index]);

3) Infinite loader basic idea: use IntersectionObserver on a sentinel or use the onItemsRendered callback from react-window to detect when to fetch more.

SEO & Voice-search ready snippet

For voice search and featured snippets, include concise answers near the top. Example Q/A snippet embedded in page: „What is React list virtualization?” — „Virtualization renders only visible items of a large list to reduce DOM nodes and improve scroll performance.” Keep answers short and directly worded.

Suggested microdata (FAQ and Article JSON-LD are already included at the top of this document).

Use H1 for the page’s main statement and H2 for narrow subtopics. Keep the first 50–160 words succinct for better snippet potential.


Semantic core (clusters)


  

Final FAQ (3 most relevant)

Q: When should I virtualize a list in React?
A: Virtualize when the number of items causes slow initial render, high memory usage, or janky scrolling — typically when you have hundreds to thousands of items. If the list is small and uncomplicated, skip the complexity.

Q: How do I handle variable-height items?
A: Measure items with ResizeObserver or use libraries with measurement helpers (react-virtualized’s CellMeasurer). Cache sizes and update the virtual list when heights change to avoid jumps.

Q: Can I use infinite scroll with virtualization?
A: Yes — combine an infinite loader with your virtual list. Trigger data fetches when items near the end render, show placeholder rows while loading, and append data to the list.