How To Virtualize Large Lists Using React Window

Transform sluggish interfaces into responsive experiences by rendering only visible items with React Window's lightweight virtualization library.

The Problem with Traditional List Rendering

When building React applications that display large collections of data, developers often encounter performance bottlenecks that emerge from how browsers handle the Document Object Model. Rendering a list of 10,000 items using conventional React patterns means creating 10,000 DOM nodes simultaneously--a approach that consumes substantial memory and triggers expensive layout calculations.

Performance Issues from Naive Rendering

  • Memory Usage: Grows linearly with list size, potentially reaching hundreds of megabytes for extremely large datasets
  • Initial Render Time: Increases proportionally, creating noticeable delays when users navigate to data-intensive views
  • Scrolling Performance: Degrades as browsers struggle to maintain 60fps while managing thousands of off-screen elements

Common use cases that demand better approaches include data dashboards with thousands of analytics records, e-commerce product catalogs, and social media feeds that continuously append new content.

Understanding List Virtualization

List virtualization transforms how we think about rendering large collections by introducing a windowing concept. Rather than rendering all items simultaneously, virtualization maintains only a small subset of DOM nodes corresponding to what users can currently see. As users scroll, the library dynamically recycles these nodes, updating their content to display different items.

How React Window Implements Virtualization

React Window, created by Brian Vaughn, takes a minimalist approach to list virtualization. The library weighs approximately 34KB gzipped, significantly smaller than react-virtualized's 150KB+ footprint, making it suitable for performance-sensitive applications where bundle size impacts load times.

Core Components:

  • FixedSizeList: Renders items with uniform heights, offering the best performance
  • VariableSizeList: Accommodates items with varying heights
  • FixedSizeGrid and VariableSizeGrid: Extend concepts to two-dimensional layouts

The architecture consists of three key pieces: the outer container establishes the scrollable viewport, the inner container provides proper scrollable area dimensions, and the item renderer function displays content for each visible position.

For optimizing React application performance, implementing list virtualization is often one of the highest-impact improvements you can make.

Virtualization Concept
1// Traditional: All 10,000 items rendered2// DOM contains 10,000 nodes3// Memory: O(n), Performance: Slow4 5// Virtualized: Only ~20 items rendered6// DOM contains ~20 nodes (recycled)7// Memory: O(1), Performance: Fast8 9const VirtualizedList = () => (10 <List11 height={600}12 itemCount={10000}13 itemSize={50}14 width={300}15 >16 {Row}17 </List>18);

Installing and Setting Up React Window

Getting started with react-window requires only a simple npm installation. The library works with React 16.8 and newer versions, supporting both class components and function components with hooks. Since version 2.0, react-window also provides ES module exports for tree-shaking support, further optimizing bundle size in modern build configurations.

npm install react-window

# For TypeScript projects
npm install @types/react-window

Import Pattern:

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

The as List renaming pattern provides clearer component names while avoiding conflicts with the HTML list element type. This convention becomes particularly valuable when using react-window alongside UI component libraries that might export their own List components.

If you're working with Next.js applications, react-window integrates seamlessly for client-side rendering of large datasets.

Creating Your First Virtualized List
1import React from 'react';2import { FixedSizeList as List } from 'react-window';3 4const Row = ({ index, style }) => (5 <div style={{6 ...style,7 display: 'flex',8 alignItems: 'center',9 padding: '0 16px',10 borderBottom: '1px solid #e0e0e0'11 }}>12 <span>Item {index}</span>13 </div>14);15 16const VirtualizedList = () => (17 <List18 height={600}19 itemCount={1000}20 itemSize={50}21 width={300}22 >23 {Row}24 </List>25);26 27export default VirtualizedList;

Variable-Size Lists

For items with varying heights, use VariableSizeList with an additional height calculation function:

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

const getItemSize = (index) => {
 // Return height based on item characteristics
 return index % 2 === 0 ? 80 : 40;
};

const VariableList = () => (
 <List
 height={600}
 itemCount={1000}
 itemSize={getItemSize}
 width={300}
 >
 {({ index, style }) => (
 <div style={style}>
 Variable height item {index}
 </div>
 )}
 </List>
);

Important: The style object passed to row components must be applied to the outermost element--this is critical for proper virtualization positioning. When building React Native applications, similar virtualization principles apply to flat lists.

For content with truly dynamic heights, you'll need to implement measurement logic using refs and useEffect hooks to track actual rendered dimensions.

Building Virtualized Grids

React Window supports two-dimensional grid layouts for virtualized tables and image galleries:

import { FixedSizeGrid as Grid } from 'react-window';

const Cell = ({ columnIndex, rowIndex, style }) => (
 <div style={{
 ...style,
 display: 'flex',
 alignItems: 'center',
 justifyContent: 'center'
 }}>
 Cell ({rowIndex}, {columnIndex})
 </div>
);

const VirtualizedGrid = () => (
 <Grid
 columnCount={50}
 columnWidth={100}
 height={600}
 rowCount={100}
 rowHeight={50}
 width={800}
 >
 {Cell}
 </Grid>
);

Grid components require specifying both row and column dimensions independently, with the cell renderer receiving both columnIndex and rowIndex parameters. This approach works well for displaying tabular data, image grids, or any two-dimensional layout where both dimensions may scroll.

When working with complex grid layouts, consider combining react-window with CSS optimization techniques to ensure smooth performance.

Implementing Infinite Scrolling

Pair react-window with react-virtualized's InfiniteLoader for infinite scrolling functionality:

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

const InfiniteList = ({ 
 hasNextPage, 
 isNextPageLoading, 
 nextItems 
}) => {
 const itemCount = hasNextPage
 ? nextItems.length + 1
 : nextItems.length;

 const loadMoreItems = isNextPageLoading
 ? () => {}
 : (startIndex, stopIndex) => fetchMoreData();

 const isItemLoaded = index => {
 if (hasNextPage) {
 return index < nextItems.length;
 }
 return true;
 };

 return (
 <InfiniteLoader
 isItemLoaded={isItemLoaded}
 itemCount={itemCount}
 loadMoreItems={loadMoreItems}
 >
 {({ onItemsRendered, ref }) => (
 <List
 ref={ref}
 height={600}
 itemCount={itemCount}
 itemSize={50}
 onItemsRendered={onItemsRendered}
 width={300}
 >
 {Item}
 </List>
 )}
 </InfiniteLoader>
 );
};

The InfiniteLoader component manages loading state and triggers additional data fetching when users scroll near the end of the loaded content. For production implementations, add debouncing to prevent excessive API calls and integrate with data fetching solutions like React Query or SWR.

Best Practices for Performance

Memoization

Memoize row components to prevent unnecessary re-renders:

import { memo } from 'react';

const Row = memo(({ index, style, data }) => {
 return (
 <div style={style}>
 {data[index].name}
 </div>
 );
}, (prev, next) => {
 return prev.index === next.index &&
 prev.data === next.data;
});

Using itemData Prop

Pass data separately using the itemData prop for better optimization:

const VirtualizedList = ({ items }) => {
 const itemData = useMemo(() => ({ items }), [items]);

 return (
 <List
 itemCount={items.length}
 itemSize={50}
 itemData={itemData}
 height={600}
 width={300}
 >
 {Row}
 </List>
 );
};

Common Pitfalls

  1. Incorrect style application: Always apply the style prop to the outermost element
  2. Variable height measurement: Implement measurement logic for unknown heights
  3. Scroll position restoration: Handle explicitly when data changes

For more on optimizing React rendering performance, explore our guide on why to avoid inline styling in production.

Comparing React Window Alternatives

react-virtualized

  • More feature-rich but larger (150KB+ vs 34KB)
  • Pre-built components for various list types
  • Choose when needing advanced features like cell measurement or column resizing

react-virtuoso

  • Modern alternative handling variable-height items automatically
  • Built-in support for sticky headers and groups
  • Prioritizes developer experience

TanStack Virtual

  • Headless, framework-agnostic virtualization
  • Works with React, Vue, and other frameworks
  • TypeScript-first approach

Recommendation: For most projects, react-window remains excellent--lightweight, well-maintained, and provides core virtualization functionality. Teams needing more features may prefer react-virtuoso, while those exploring modern React optimization might find TanStack Virtual's architecture appealing.

If you're building cross-platform applications, consider how each library's architecture supports your target platforms.

Conclusion

React Window provides an essential tool for building performant React applications that display large datasets. By rendering only visible items and dynamically updating content as users scroll, it transforms sluggish interfaces into responsive experiences. The library's lightweight footprint (34KB gzipped), flexible architecture, and active maintenance make it suitable for production applications across industries--from data-intensive dashboards to consumer-facing product catalogs.

Key Takeaways:

  • Virtualization reduces DOM complexity from O(n) to O(1)
  • FixedSizeList offers best performance for uniform items
  • VariableSizeList handles dynamic content with measurement
  • InfiniteLoader enables seamless infinite scrolling
  • Combine with memoization for optimal performance

Start by identifying performance bottlenecks in your existing lists, then apply virtualization where it provides meaningful improvement. The minimal API surface area means quick integration, while the library's flexibility supports complex requirements as your application evolves.

For teams building high-performance web applications, pairing React Window with proper React development practices ensures smooth user experiences even with large datasets.

Frequently Asked Questions

Build High-Performance React Applications

Our team specializes in optimizing React applications for scale, implementing virtualization, and building responsive user interfaces that handle large datasets efficiently.

Sources

  1. Spritle Software - How Virtualization with React-Window Boosts React App Performance - Comprehensive guide covering installation, basic implementation, and advantages of react-window
  2. LogRocket Blog - react-virtualized vs. react-window - Detailed technical comparison between virtualization libraries
  3. Patterns.dev - List Virtualization - Comprehensive resource on virtualization patterns including bundle size comparison
  4. react-window npm package - Official package documentation with API reference