react-infinite vs react-list vs react-tiny-virtual-list vs react-virtualized vs react-window
React List Virtualization Strategies
react-infinitereact-listreact-tiny-virtual-listreact-virtualizedreact-windowSimilar Packages:

React List Virtualization Strategies

These libraries solve the performance problem of rendering large lists in React applications. When a list contains thousands of items, rendering all DOM nodes at once causes slow load times and sluggish scrolling. These packages use "virtualization" or "windowing" techniques to only render the items currently visible in the viewport. react-virtualized and react-window are the most robust solutions, offering grid support and precise scroll handling. react-infinite focuses on infinite scroll patterns where items append as you scroll. react-list and react-tiny-virtual-list offer lighter weight alternatives but with fewer features and less active maintenance. Choosing the right one depends on whether you need complex layouts, variable item heights, or long-term support.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-infinite02,685243 kB102-BSD-3-Clause
react-list01,97434.9 kB71a year agoMIT
react-tiny-virtual-list02,488-548 years agoMIT
react-virtualized027,0692.24 MB0a year agoMIT
react-window017,183209 kB13 months agoMIT

React List Virtualization: Architecture, API, and Maintenance Compared

Rendering long lists in React can crash a browser if not handled correctly. All five packages — react-infinite, react-list, react-tiny-virtual-list, react-virtualized, and react-window — aim to solve this by rendering only visible items. However, they differ significantly in architecture, API design, and long-term viability. Let's break down how they handle common engineering challenges.

šŸ—ļø Core Rendering Architecture

The fundamental difference lies in how they manage the DOM. Some use "windowing" (recycling nodes in a fixed container), while others use "infinite scroll" (appending nodes).

react-window uses a windowing approach. It keeps the container height fixed and moves the content inside using transforms.

// react-window: FixedSizeList
import { FixedSizeList } from 'react-window';

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

const List = () => (
  <FixedSizeList height={400} itemCount={1000} itemSize={35}>
    {Row}
  </FixedSizeList>
);

react-virtualized also uses windowing but with a heavier API structure.

// react-virtualized: List
import { List } from 'react-virtualized';

const Row = ({ index, key, style }) => (
  <div key={key} style={style}>Row {index}</div>
);

const List = () => (
  <List
    height={400}
    rowCount={1000}
    rowHeight={35}
    rowRenderer={Row}
    width={300}
  />
);

react-infinite uses an infinite scroll approach, appending elements as you reach the bottom.

// react-infinite: Infinite
import Infinite from 'react-infinite';

const List = () => (
  <Infinite
    elementHeight={35}
    containerHeight={400}
    onInfiniteLoad={() => loadMoreItems()}
  >
    {items.map(item => <div key={item.id}>{item.name}</div>)}
  </Infinite>
);

react-list uses a windowing approach optimized for simplicity.

// react-list: List
import List from 'react-list';

const Item = (key, index) => <div key={key}>Row {index}</div>;

const List = () => (
  <List
    itemRenderer={Item}
    length={1000}
    useStaticItemDimensions={true}
  />
);

react-tiny-virtual-list uses a simple windowing approach for fixed sizes.

// react-tiny-virtual-list: VirtualList
import VirtualList from 'react-tiny-virtual-list';

const List = () => (
  <VirtualList
    width={300}
    height={400}
    itemSize={35}
    items={items}
    renderItem={({ index, style }) => (
      <div style={style}>Row {index}</div>
    )}
  />
);

šŸ“ Handling Variable Item Heights

Support for items with different heights is a common requirement. This is where the older or simpler libraries often fall short.

react-window supports this via VariableSizeList. You must manually manage height caching.

// react-window: VariableSizeList
import { VariableSizeList } from 'react-window';

const List = () => (
  <VariableSizeList
    height={400}
    itemCount={items.length}
    itemSize={index => getHeightForItem(index)}
  >
    {Row}
  </VariableSizeList>
);

react-virtualized supports this via CellMeasurer or dynamic row heights.

// react-virtualized: Dynamic Row Height
import { List, CellMeasurer, CellMeasurerCache } from 'react-virtualized';

const cache = new CellMeasurerCache({ fixedWidth: true });

const Row = ({ index, key, parent, style }) => (
  <CellMeasurer cache={cache} index={index} key={key} parent={parent}>
    {({ measure, content }) => (
      <div style={style} onLoad={measure}>{content}</div>
    )}
  </CellMeasurer>
);

react-infinite does not support variable heights natively. You must approximate heights or use uniform containers.

// react-infinite: No native support
// Developers often wrap items in fixed-height containers
// to prevent layout shifts, limiting design flexibility.

react-list supports variable heights but requires specific configuration.

// react-list: Variable Dimensions
import List from 'react-list';

const List = () => (
  <List
    itemRenderer={Item}
    length={1000}
    useStaticItemDimensions={false}
  />
);

react-tiny-virtual-list does not support variable item heights. It requires a fixed itemSize prop.

// react-tiny-virtual-list: Fixed Size Only
// The API requires a single number for itemSize.
// Variable heights are not supported.

šŸ”„ Infinite Loading Patterns

Loading more data as the user scrolls is distinct from virtualizing existing data. Some libraries blend these concerns.

react-window separates virtualization from data loading. You wrap it with an external loader or use react-window-infinite-loader.

// react-window: With Infinite Loader
import { FixedSizeList } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';

const List = () => (
  <InfiniteLoader
    isItemLoaded={isItemLoaded}
    loadMoreItems={loadMoreItems}
    itemCount={items.length}
  >
    {({ onItemsRendered, ref }) => (
      <FixedSizeList
        itemCount={items.length}
        itemSize={35}
        onItemsRendered={onItemsRendered}
        ref={ref}
      >
        {Row}
      </FixedSizeList>
    )}
  </InfiniteLoader>
);

react-virtualized includes InfiniteLoader as a built-in component.

// react-virtualized: InfiniteLoader
import { InfiniteLoader, List } from 'react-virtualized';

const List = () => (
  <InfiniteLoader
    isRowLoaded={({ index }) => !!items[index]}
    loadMoreRows={loadMoreItems}
    rowCount={items.length}
  >
    {({ onRowsRendered, registerChild }) => (
      <List
        onRowsRendered={onRowsRendered}
        ref={registerChild}
        rowCount={items.length}
        rowHeight={35}
        rowRenderer={Row}
      />
    )}
  </InfiniteLoader>
);

react-infinite has infinite loading built into the core component.

// react-infinite: Built-in Loading
import Infinite from 'react-infinite';

const List = () => (
  <Infinite
    elementHeight={35}
    containerHeight={400}
    onInfiniteLoad={() => loadMoreItems()}
    loadingSpinner={LoadingComponent}
  >
    {items.map(item => <div key={item.id}>{item.name}</div>)}
  </Infinite>
);

react-list does not have built-in infinite loading. You must handle data fetching externally.

// react-list: External Loading
// Developers must manage state and append items to the
// list manually before passing them to the List component.

react-tiny-virtual-list does not have built-in infinite loading. External state management is required.

// react-tiny-virtual-list: External Loading
// Similar to react-list, you must manage the items array
// and pass the updated list to the VirtualList component.

šŸ› ļø Maintenance and Future Proofing

Choosing a library is not just about features — it is about who will fix bugs next year.

react-window is actively maintained by Brian Vaughn at Vercel. It receives updates for React concurrency features and bug fixes. It is the safest bet for new projects.

react-virtualized is in maintenance mode. The author recommends react-window for new work. It will not receive new features, only critical fixes.

react-infinite has seen minimal activity in recent years. It is considered legacy software. Using it introduces risk for security patches or React compatibility.

react-list has not seen significant updates in years. It is stable but stagnant. Teams should plan to migrate away from it during major refactors.

react-tiny-virtual-list is also stagnant. While the code is simple and less likely to break, it lacks support for modern React patterns like hooks or concurrent rendering.

šŸ“Š Summary Table

Featurereact-windowreact-virtualizedreact-infinitereact-listreact-tiny-virtual-list
ArchitectureWindowingWindowingInfinite ScrollWindowingWindowing
Variable Heightsāœ… Yesāœ… YesāŒ Noāœ… YesāŒ No
Infinite Loadāš ļø Add-onāœ… Built-ināœ… Built-ināŒ ExternalāŒ External
Grid Supportāœ… Yesāœ… YesāŒ NoāŒ NoāŒ No
Maintenance🟢 Active🟔 MaintenancešŸ”“ LegacyšŸ”“ LegacyšŸ”“ Legacy

šŸ’” The Big Picture

react-window is the modern standard šŸ†. It balances performance, bundle size, and API simplicity. Use this for almost all new development.

react-virtualized is the legacy enterprise choice šŸ¢. Stick with it only if you are already deeply invested in its ecosystem or need specific components like Collection.

react-infinite, react-list, and react-tiny-virtual-list are legacy tools šŸ•°ļø. They may work for small, static projects, but they lack the active support and feature depth required for complex modern applications. Migrating to react-window is the recommended path for long-term health.

How to Choose: react-infinite vs react-list vs react-tiny-virtual-list vs react-virtualized vs react-window

  • react-infinite:

    Choose react-infinite if you are maintaining a legacy application that already depends on it and specifically needs an infinite scroll pattern where items append to the DOM. It is not recommended for new projects because it lacks the performance optimizations of modern windowing libraries and has limited maintenance. Use this only if you need to load more data automatically as the user scrolls down without a fixed container height.

  • react-list:

    Choose react-list if you are working on an older codebase that already implements it and requires a simple, lightweight virtualization solution for uniform lists. It is not suitable for new development due to its age and lack of recent updates. Avoid this package if you need support for variable item heights or complex grid layouts, as it focuses on basic list rendering.

  • react-tiny-virtual-list:

    Choose react-tiny-virtual-list if you need a very small bundle size for a simple list with fixed item heights and cannot afford the dependencies of larger libraries. It is best for small-scale projects or embedded widgets where performance overhead must be minimal. Do not use this for complex applications requiring variable row heights, sticky headers, or active long-term support.

  • react-virtualized:

    Choose react-virtualized if you are maintaining an existing enterprise application that relies on its extensive feature set, including complex grids and collection views. It is a mature library but is now in maintenance mode, meaning new features are unlikely. For new projects, consider migrating to react-window unless you need specific legacy components like Collection that are not available elsewhere.

  • react-window:

    Choose react-window for all new projects requiring list or grid virtualization, as it is the modern successor to react-virtualized with a smaller bundle size and simpler API. It is actively maintained and supports both fixed and variable item sizes efficiently. This is the standard choice for teams looking for stability, performance, and long-term compatibility with modern React versions.

README for react-infinite

React Infinite

Build Status Coverage Status npm version bitHound Score

A browser-ready efficient scrolling container based on UITableView.

React Infinite 0.7.1 only supports React 0.14 and above. Please pin your package to 0.6.0 for React 0.13 support.

When a long list of DOM elements are placed in a scrollable container, all of them are kept in the DOM even when they are out the user's view. This is highly inefficient, especially in cases when scrolling lists can be tens or hundreds of thousands of items long. React Infinite solves this by rendering only DOM nodes that the user is able to see or might soon see. Other DOM nodes are clustered and rendered as a single blank node.

Installation

In the Browser

The relevant files are dist/react-infinite.js and dist/react-infinite.min.js. You must have React available as a global variable named React on the window. Including either file, through concatenation or a script tag, will produce a global variable named Infinite representing the component.

In NPM

React Infinite uses a Universal Module Definition so you can use it in NPM as well. npm install this package and

var Infinite = require('react-infinite');

In Browserify

If you want to use the source with Browserify, the ES5-compiled source is directly requirable from the /build folder off NPM.

Otherwise, you can follow the instructions for NPM.

Basic Use

Elements of Equal Height

To use React Infinite with a list of elements you want to make scrollable, provide them to React Infinite as children.

<Infinite containerHeight={200} elementHeight={40}>
    <div className="one"/>
    <div className="two"/>
    <div className="three"/>
</Infinite>

Elements of Varying Heights

If not all of the children have the same height, you must provide an array of integers to the elementHeight prop instead.

<Infinite containerHeight={200} elementHeight={[111, 252, 143]}>
    <div className="111-px"/>
    <div className="252-px"/>
    <div className="143-px"/>
</Infinite>

Using the Window to Scroll (useWindowAsScrollContainer mode)

To use the entire window as a scroll container instead of just a single div (thus using window.scrollY instead of a DOM element's scrollTop), add the useWindowAsScrollContainer prop.

<Infinite containerHeight={200} elementHeight={[111, 252, 143]}
          useWindowAsScrollContainer>
    <div className="111-px"/>
    <div className="252-px"/>
    <div className="143-px"/>
</Infinite>

As A Chat or Message Box (displayBottomUpwards mode)

React Infinite now supports being used as a chat box, i.e. appended elements appear at the bottom when added, and the loading of the next page occurs when the user scrolls to the top of the container. To do so, simply add the displayBottomUpwards prop. A sample implementation can be consulted for more information - run gulp develop to compile the example files.

<Infinite containerHeight={200} elementHeight={[111, 252, 143]}
          displayBottomUpwards>
    // insert messages for subsequent pages at this point
    <div className="third-latest-chat"/>
    <div className="second-latest-chat"/>
    <div className="latest-chat-message"/>
</Infinite>

Note on Smooth Scrolling

A wrapper div is applied that disables pointer events on the children for a default of 150 milliseconds after the last user scroll action for browsers with inertial scrolling. To configure this, set timeScrollStateLastsForAfterUserScrolls.

Static Methods

Function Infinite.containerHeightScaleFactor(Number number)

This function allows a value to be specified for preloadBatchSize and preloadAdditionalHeight that is a relative to the container height. Please see the documentation for those two configuration options for further information on how to use it.

Configuration Options

Children

The children of the <Infinite> element are the components you want to render. This gives you as much flexibility as you need in the presentation of those components. Each child can be a different component if you desire. If you wish to render a set of children not all of which have the same height, you must map each component in the children array to an number representing its height and pass it in as the elementHeight prop.

Major Display Modes

By default, React Infinite renders a single element with the provided containerHeight, and the list sticks to the top like a regular table. However, you can choose to use the entire window as the scroll container or make React Infinite like a chatbox with the following options. They can be used together if you wish.

Bool useWindowAsScrollContainer

Defaults to false. This option allows the window to be used as the scroll container, instead of an arbitrary div, when it is set to true. This means that scroll position is detected by window.scrollY instead of the scrollTop of the div that React Infinite creates. Using this option is a way of achieving smoother scrolling on mobile before the problem is solved for container divs.

Bool displayBottomUpwards

Defaults to false. This allows React Infinite to be used as a chatbox. This means that the scroll is stuck to the bottom by default, and the user scrolls up to the top of the container to load the next page. The children are displayed in the same order.

Configuration Options

(Required) Number | [Number] elementHeight

If each child element has the same height, you can pass a number representing that height as the elementHeight prop. If the children do not all have the same height, you can pass an array which is a map the children to numbers representing their heights to the elementHeight prop.

Number containerHeight

The height of the scrolling container in pixels. This is a required prop if useWindowAsScrollContainer is not set to true.

Number | Object preloadBatchSize

Defaults to this.props.containerHeight * 0.5. Imagine the total height of the scrollable divs. Now divide this equally into blocks preloadBatchSize pixels high. Every time the container's scrollTop enters each of these blocks the set of elements rendered in full are those contained within the block and elements that are within preloadAdditionalHeight above and below it.

When working with the window as the scroll container, it is sometimes useful to specify a scale factor relative to the container height as the batch size, so your code does not need to know anything about the window. To do this, use Infinite.containerHeightScaleFactor. So, for example, if you want the preloaded batch size to be twice the container height, write preloadBatchSize={Infinite.containerHeightScaleFactor(2)}.

Number | Object preloadAdditionalHeight

Defaults to this.props.containerHeight. The total height of the area in which elements are rendered in full is height of the current scroll block (see preloadBatchSize) as well as preloadAdditionalHeight above and below it.

When working with the window as the scroll container, it is sometimes useful to specify this relative to the container height. If you want the preloaded additional height to be twice the container height, write preloadAdditionalHeight={Infinite.containerHeightScaleFactor(2)}. Please see preloadBatchSize for more details.

Function handleScroll(DOMNode node)

Defaults to function(){}. A function that is called when the container is scrolled, i.e. when the onScroll event of the infinite scrolling container is fired. The only argument passed to it is the native DOM Node of the scrolling container.

Number infiniteLoadBeginEdgeOffset

Defaults to undefined, which means that infinite loading is disabled. To disable infinite loading, do not provide this property or set it to undefined.

Regular Mode When the user reaches this number of pixels from the bottom, the infinite load sequence will be triggered by showing the infinite load spinner delegate and calling the function onInfiniteLoad.

displayBottomUpwards mode When the user reaches this number of pixels from the top of the container, the infinite load sequence will be triggered by showing the infinite loading spinner delegate at the top of the container and calling onInfiniteLoad.

Function onInfiniteLoad()

Defaults to function(){}. This function is called when the scroll exceeds infiniteLoadBeginEdgeOffset. Before this function is called, the infinite loading spinner is automatically turned on. You can set up infinite scrolling with this function like this:

  1. Fetch a new page of records from the appropriate API
  2. When the AJAX call returns, send the new list of elements (with the items that were just fetched) back as the children of React Infinite.
  3. Set React Infinite's isInfiniteLoading prop to false to hide the loading spinner display

onInfiniteLoad relies heavily on passing props as a means of communication in the style of idiomatic React.

React Node loadingSpinnerDelegate

Defaults to <div/>. The element that is provided is used to render the loading view when React Infinite's isInfiniteLoading property is set to true. A React Node is anything that satisfies React.PropTypes.node.

Bool isInfiniteLoading

Defaults to false. This property determines whether the infinite spinner is showing.

Number timeScrollStateLastsForAfterUserScrolls

Defaults to 150 (in milliseconds). On Apple and some other devices, scroll is inertial. This means that the window continues to scroll for several hundred milliseconds after an onScroll event is fired. To prevent janky behavior, we do not want pointer-events to reactivate before the window has finished moving. Setting this parameter causes the Infinite component to think that the user is still scrolling for the specified number of milliseconds after the last onScroll event is received.

String className

Allows a CSS class to be set on the scrollable container.

Sample Code

Code samples are now available in the /examples directory for your perusal. Two examples are provided, one for constant height with infinite loading and another with random variable heights with infinite loading. To generate the files necessary for the examples, execute npm install && gulp build -E. You may need to first install gulp with npm install -g gulp.

To get you started, here is some sample code that implements an infinite scroll with an simulated delay of 2.5 seconds. A live demo of this example is available on our blog.

var createReactClass = require('create-react-class');

var ListItem = createReactClass({
    render: function() {
        return <div className="infinite-list-item">
        List Item {this.props.num}
        </div>;
    }
});

var InfiniteList = createReactClass({
    getInitialState: function() {
        return {
            elements: this.buildElements(0, 20),
            isInfiniteLoading: false
        }
    },

    buildElements: function(start, end) {
        var elements = [];
        for (var i = start; i < end; i++) {
            elements.push(<ListItem key={i} num={i}/>)
        }
        return elements;
    },

    handleInfiniteLoad: function() {
        var that = this;
        this.setState({
            isInfiniteLoading: true
        });
        setTimeout(function() {
            var elemLength = that.state.elements.length,
                newElements = that.buildElements(elemLength, elemLength + 1000);
            that.setState({
                isInfiniteLoading: false,
                elements: that.state.elements.concat(newElements)
            });
        }, 2500);
    },

    elementInfiniteLoad: function() {
        return <div className="infinite-list-item">
            Loading...
        </div>;
    },

    render: function() {
        return <Infinite elementHeight={40}
                         containerHeight={250}
                         infiniteLoadBeginEdgeOffset={200}
                         onInfiniteLoad={this.handleInfiniteLoad}
                         loadingSpinnerDelegate={this.elementInfiniteLoad()}
                         isInfiniteLoading={this.state.isInfiniteLoading}
                         >
            {this.state.elements}
        </Infinite>;
    }
});

ReactDOM.render(<InfiniteList/>, document.getElementById('react-example-one'));

SeatGeek also currently uses React Infinite in production on our event pages; because we only have pages for events in the future, a link would not be appropriate. To see one, head to one of our team pages for the New York Giants, or the New York Mets, or the New York Knicks, and click on the green button for an event to see them in action in the Omnibox.

Contributing to React Infinite

Useful notes for how to contribute are available.