react-window: Fast Virtualized Lists in React — Guide & Examples
Quick summary: react-window is a compact, high-performance library for rendering large lists and grids by windowing — only rendering what's visible. This guide covers installation, FixedSizeList & VariableSizeList examples, common patterns (infinite scroll, memoization), and optimization tips you can apply immediately.
What react-window is and when to use it
react-window is a light-weight virtualization library created by Brian Vaughn that dramatically reduces DOM node count for long lists and large grids. Instead of rendering every row or cell, it only renders the subset currently visible in the viewport (plus an optional overscan buffer), which improves rendering speed and memory usage.
Use react-window when you must render thousands of rows, complex item UIs, or many grid cells where off-screen items dominate DOM cost. It pairs well with data fetching patterns and techniques like infinite scroll, because it keeps update and layout costs bounded regardless of list length.
However, react-window assumes items have predictable sizing patterns (fixed or variable known ahead) and offers simpler APIs than heavier libraries. For highly dynamic item sizes that change during render or require inline measurement, you may need additional utilities or an alternate approach.
Key components and core API
The two primary list primitives are FixedSizeList and VariableSizeList. FixedSizeList is the simplest: every item has the same height (or width for horizontal lists). VariableSizeList supports per-item sizes supplied via a getSize(index) callback and allows dynamic sizes at creation time.
Each list receives props like height, width, itemCount, and itemSize (or itemSize accessor). The render prop (children) receives {index, style, data} where style must be attached to the item wrapper so react-window can position items using absolute positioning and transforms.
Other utilities include FixedSizeGrid / VariableSizeGrid for 2D layouts, and helper hooks to manage scrolling and list refs. The API is intentionally small to minimize bundle impact and make it easy to reason about layout and performance.
Installation and setup (getting started)
Install react-window via npm or yarn. It’s tiny (~2KB gzipped) and integrates directly into any React app:
npm install react-window
# or
yarn add react-window
After installing, import the component you need. Keep a single list component per DOM container and avoid nesting too many lists unless necessary. Always set explicit height/width for the list container so the library can compute visible ranges reliably.
For a quick tutorial and step-by-step example, see this comprehensive react-window tutorial.
Minimal FixedSizeList example
Below is a compact FixedSizeList example showing the required structure: specify size, count, and a row renderer that applies the provided style prop.
import React from 'react';
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);
export default function App() {
return (
<List
height={400}
itemCount={10000}
itemSize={35}
width={'100%'}
>
{Row}
</List>
);
}
Key points: attach the style to the row wrapper, avoid complex inline handlers that re-create on every render, and memoize row content if rendering expensive components.
For lists with variable heights, use VariableSizeList and provide an itemSize callback or sizes array. Maintain a reference to the list and call resetAfterIndex(index) if sizes change for items after an index.
Infinite scroll and data loading patterns
Infinite scroll with react-window is typically done by combining it with a sentinel pattern or checking the visible range. Since the list tracks visible indices, you can detect when the user is near the end and trigger a fetch for more items.
Strategy: keep your data array length synced with itemCount. As you append new items, update itemCount and let react-window re-render. Use an overscanCount to pre-render a few off-screen rows to reduce perceived fetch latency.
A common approach is to use an effect that monitors the range from onItemsRendered (provided by List via a ref). When the trailing index approaches itemCount – threshold, dispatch a data fetch. Example pseudo-flow: onItemsRendered → if visibleStopIndex >= itemCount – 5 → fetchMore().
Performance optimization and best practices
Even though react-window is performant by design, you should still apply standard React performance hygiene: memoize row components with React.memo, avoid inline style objects and functions where possible, and limit expensive child renders.
Use overscan to balance smoothness vs. extra rendering. For jitter-free scrolling, set overscanCount to a small number (2–5) for lists with cheap items; bump it up if items are heavy or fetch latency is high. Be mindful of CSS (transforms are faster than top/left) and prefer will-change or translateZ hacks only when necessary.
If your rows contain images or dynamic height content, consider fixed placeholders or virtualization-friendly layouts to avoid layout thrashing. For dynamic height lists, use VariableSizeList and measure items once (caching sizes) instead of measuring on every render.
- When to use react-window: thousands of rows, grids, or repeatable items where off-screen DOM is the bottleneck.
- When not to use it: small lists, highly dynamic inline-resized items without a measurement strategy, or when accessibility requires render-all semantics.
Troubleshooting and common gotchas
Missing styles: If rows stack or overlap, ensure you attach the style prop returned by react-window to the exact element that’s the list item container. These styles set absolute positioning and transforms that place items precisely.
Dynamic heights: If item sizes change after initial render, call listRef.current.resetAfterIndex(index, shouldForceUpdate) so react-window re-measures subsequent items. For frequent size changes, consider alternatives or implement a size-caching and measurement layer.
Server-side rendering: react-window is client-only for measurement; server rendering will produce empty or unmeasured containers. Hydration should assume a client-only mount for lists or render a small server-safe fallback.
Advanced patterns: grids, nested lists, and accessibility
react-window provides Grid components (FixedSizeGrid, VariableSizeGrid) for 2D layouts. Use them when you have a matrix of homogeneous cells. Grids follow the same render-prop pattern and require explicit width/height and column/row sizes.
Nested lists are possible but can complicate virtualization if both dimensions need virtualization. In most cases, favor a single list with richer row rendering over nested virtualized lists to keep visible counts predictable.
Accessibility: since virtualized lists unmount off-screen items, ensure keyboard focus and screen readers still have a coherent experience. Provide aria-live regions for critical updates and ensure focusable elements don’t get unmounted while focused (trap focus or scroll into view before unmounting).
Conclusion — when react-window is the right choice
react-window is an excellent choice for most virtualization needs: tiny bundle footprint, simple API, and great runtime performance. It’s ideal for lists and grids with predictable sizing and when DOM count or repaint cost is the performance bottleneck.
Pair it with memoization, cautious overscan, and clear data-fetching patterns (infinite scroll) to get smooth, responsive UIs even with very large datasets. When you need advanced measurement or highly dynamic sizing, consider composing measurement utilities or exploring sibling libraries that provide more built-in measurement features.
For additional examples and a full tutorial, check this in-depth react-window tutorial, and the upstream repo at react-window on GitHub.
FAQ
1. How do I install and get started with react-window?
Install via npm or yarn (npm install react-window), import FixedSizeList or VariableSizeList, set explicit width/height and itemCount, and render your item component with the supplied style prop. See the Getting Started tutorial for a step-by-step example: react-window tutorial.
2. What’s the difference between react-window and react-virtualized?
react-window is a lighter, smaller library focused purely on fast virtualization with a minimal API. react-virtualized is older and feature-rich (measurements, cell caching, utils). Choose react-window for simplicity and lower bundle size, and react-virtualized when you need more out-of-the-box utilities and complex measurement behaviors.
3. How can I implement infinite scroll with react-window?
Use the list's onItemsRendered callback or the list ref to watch visible indices. When the visibleStopIndex is within a threshold of itemCount – 1, trigger an async fetch and append items to your data array. Keep itemCount updated so react-window can include newly loaded rows. Use overscanCount to pre-render a few off-screen rows and reduce perceived latency.
