react-infinite-scroll-component vs react-tiny-virtual-list vs react-virtualized vs react-window
React における無限スクロールと仮想リストの実装比較
react-infinite-scroll-componentreact-tiny-virtual-listreact-virtualizedreact-window類似パッケージ:

React における無限スクロールと仮想リストの実装比較

react-infinite-scroll-componentreact-tiny-virtual-listreact-virtualizedreact-window は、すべて React アプリケーションで大規模なリストや無限スクロール機能を実装するためのライブラリですが、それぞれアプローチと設計思想が異なります。

react-infinite-scroll-component は、スクロール検知とデータローディングに特化したシンプルなコンポーネントで、仮想化機能は提供しません。すべてのアイテムが DOM にレンダリングされるため、アイテム数が増えるとパフォーマンスが低下します。

react-tiny-virtual-list は、最小限の依存関係で仮想スクロールを実現する軽量ライブラリです。固定高さのアイテムに最適化されており、設定がシンプルですが機能は限定的です。

react-virtualized は、長年愛されてきた包括的な仮想化ライブラリで、グリッド、テーブル、リストなど多様なコンポーネントを提供します。しかし、現在はメンテナンスモードに入り、新しいプロジェクトでの使用は推奨されていません。

react-window は、react-virtualized の作者によって作成された後継ライブラリで、よりモダンな設計と軽量な実装が特徴です。新しいプロジェクトではこちらが推奨されます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
react-infinite-scroll-component03,073199 kB1983日前MIT
react-tiny-virtual-list02,493-548年前MIT
react-virtualized027,0682.24 MB01年前MIT
react-window017,143209 kB12ヶ月前MIT

React 無限スクロール・仮想リストライブラリ完全比較

大規模なリストデータを React アプリケーションで扱う際、パフォーマンスとユーザー体験の両立が課題になります。react-infinite-scroll-componentreact-tiny-virtual-listreact-virtualizedreact-window は、この問題を解決するための代表的なライブラリですが、それぞれ設計思想と適したユースケースが異なります。

🎯 基本アプローチ:仮想化の有無

最も重要な違いは、リストの仮想化(virtualization)をサポートしているかです。

react-infinite-scroll-component は仮想化を提供しません。すべてのアイテムが DOM にレンダリングされるため、アイテム数が増えるとパフォーマンスが低下します。

// react-infinite-scroll-component
import InfiniteScroll from 'react-infinite-scroll-component';

function UserList() {
  const [items, setItems] = useState([]);
  
  const fetchMoreData = () => {
    // API から追加データを取得
  };

  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={true}
      loader={<h4>読み込み中...</h4>}
    >
      {items.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </InfiniteScroll>
  );
}

react-tiny-virtual-listreact-virtualizedreact-window は仮想化をサポートします。表示されているアイテムのみをレンダリングするため、数千件以上のデータでもパフォーマンスを維持できます。

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

function UserList() {
  return (
    <VirtualList
      width={500}
      height={300}
      itemCount={items.length}
      itemSize={50}
      renderItem={({ index, style }) => (
        <div key={items[index].id} style={style}>
          {items[index].name}
        </div>
      )}
    />
  );
}
// react-virtualized
import { List } from 'react-virtualized';

function UserList() {
  return (
    <List
      width={500}
      height={300}
      rowCount={items.length}
      rowHeight={50}
      rowRenderer={({ index, key, style }) => (
        <div key={key} style={style}>
          {items[index].name}
        </div>
      )}
    />
  );
}
// react-window
import { FixedSizeList } from 'react-window';

function UserList() {
  return (
    <FixedSizeList
      width={500}
      height={300}
      itemCount={items.length}
      itemSize={50}
    >
      {({ index, style }) => (
        <div style={style}>
          {items[index].name}
        </div>
      )}
    </FixedSizeList>
  );
}

📐 アイテム高さ:固定 vs 可変

アイテムの高さが固定か可変かも、ライブラリ選択の重要な要素です。

react-tiny-virtual-list は固定高さのアイテムに特化しています。可変高さのアイテムには対応していません。

// react-tiny-virtual-list - 固定高さのみ
<VirtualList
  itemSize={50} // 固定値のみ
  renderItem={({ index, style }) => (...)}
/>

react-virtualized は固定・可変両方に対応しています。可変高さの場合は CellMeasurer が必要です。

// react-virtualized - 可変高さ対応
import { List, CellMeasurer, CellMeasurerCache } from 'react-virtualized';

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

<List
  rowHeight={({ index }) => cache.rowHeight({ index })}
  rowRenderer={({ index, key, style, parent }) => (
    <CellMeasurer
      key={key}
      cache={cache}
      parent={parent}
      columnIndex={0}
      rowIndex={index}
    >
      {({ measure }) => (
        <div style={style} onLoad={measure}>
          {items[index].content}
        </div>
      )}
    </CellMeasurer>
  )}
/>

react-window も固定・可変両方に対応しています。可変高さの場合は VariableSizeList を使用します。

// react-window - 可変高さ対応
import { VariableSizeList } from 'react-window';

const listRef = useRef();

<VariableSizeList
  width={500}
  height={300}
  itemCount={items.length}
  itemSize={index => getItemHeight(items[index])}
  ref={listRef}
>
  {({ index, style }) => (
    <div style={style}>
      {items[index].content}
    </div>
  )}
</VariableSizeList>

react-infinite-scroll-component は仮想化しないため、高さの制限はありませんが、パフォーマンスの制約があります。

// react-infinite-scroll-component - 高さ制限なし(ただし非仮想化)
<InfiniteScroll dataLength={items.length} next={fetchMore} hasMore={true}>
  {items.map(item => (
    <div key={item.id}>
      {/* 任意の高さのコンテンツ */}
    </div>
  ))}
</InfiniteScroll>

🔄 無限スクロール機能

無限スクロール機能の提供状況も異なります。

react-infinite-scroll-component は無限スクロール機能がコア機能として組み込まれています。スクロール検知、データローディング、ローディング表示がすべて提供されます。

// react-infinite-scroll-component - 無限スクロール機能内蔵
<InfiniteScroll
  dataLength={items.length}
  next={fetchMoreData}
  hasMore={hasMore}
  loader={<Spinner />}
  endMessage={<p>データがありません</p>}
  scrollThreshold={0.8}
>
  {items.map(item => <Item key={item.id} data={item} />)}
</InfiniteScroll>

react-tiny-virtual-listreact-virtualizedreact-window は仮想化に特化しており、無限スクロール機能は提供していません。別途実装が必要です。

// react-window - 無限スクロールは別途実装
const [items, setItems] = useState([]);
const [hasMore, setHasMore] = useState(true);

const loadMore = () => {
  if (!hasMore) return;
  fetchMore().then(newItems => {
    setItems(prev => [...prev, ...newItems]);
    if (newItems.length < PAGE_SIZE) setHasMore(false);
  });
};

<FixedSizeList
  itemCount={items.length}
  itemSize={50}
  onItemsRendered={({ visibleStopIndex }) => {
    if (visibleStopIndex >= items.length - 5) loadMore();
  }}
>
  {({ index, style }) => (
    <div style={style}>{items[index].name}</div>
  )}
</FixedSizeList>
// react-virtualized - 無限スクロールは別途実装
<List
  rowCount={items.length}
  rowHeight={50}
  onRowsRendered={({ stopIndex }) => {
    if (stopIndex >= items.length - 5) loadMore();
  }}
  rowRenderer={({ index, key, style }) => (
    <div key={style}>{items[index].name}</div>
  )}
/>
// react-tiny-virtual-list - 無限スクロールは別途実装
<VirtualList
  itemCount={items.length}
  itemSize={50}
  renderScrollContainer={props => (
    <div
      {...props}
      onScroll={e => {
        props.onScroll(e);
        // 無限スクロールロジックをここに追加
      }}
    />
  )}
  renderItem={({ index, style }) => (
    <div style={style}>{items[index].name}</div>
  )}
/>

📦 提供コンポーネントの種類

各ライブラリが提供するコンポーネントの種類も異なります。

react-infinite-scroll-component は単一の InfiniteScroll コンポーネントのみを提供します。

// react-infinite-scroll-component - 単一コンポーネント
import InfiniteScroll from 'react-infinite-scroll-component';
<InfiniteScroll>{/* 任意のコンテンツ */}</InfiniteScroll>

react-tiny-virtual-list も単一の VirtualList コンポーネントのみを提供します。

// react-tiny-virtual-list - 単一コンポーネント
import VirtualList from 'react-tiny-virtual-list';
<VirtualList {/* 設定とレンダラー */} />

react-virtualized は多様なコンポーネントを提供します(現在は非推奨)。

// react-virtualized - 多様なコンポーネント
import { List, Grid, Table, Collection, AutoSizer } from 'react-virtualized';

<List>{/* 1D リスト */}</List>
<Grid>{/* 2D グリッド */}</Grid>
<Table>{/* テーブル */}</Table>
<AutoSizer>{/* 自動サイズ調整 */}</AutoSizer>

react-window も多様なコンポーネントを提供しますが、よりモダンな設計です。

// react-window - 多様なコンポーネント
import { 
  FixedSizeList, 
  VariableSizeList, 
  FixedSizeGrid, 
  VariableSizeGrid,
  AutoSizer 
} from 'react-window';

<FixedSizeList>{/* 固定高さリスト */}</FixedSizeList>
<VariableSizeList>{/* 可変高さリスト */}</VariableSizeList>
<FixedSizeGrid>{/* 固定サイズグリッド */}</FixedSizeGrid>
<AutoSizer>{/* 自動サイズ調整 */}</AutoSizer>

⚠️ メンテナンス状況と推奨

react-virtualized は現在メンテナンスモードです。公式ドキュメントと npm ページで、新しいプロジェクトでは react-window の使用が推奨されています。

// ⚠️ react-virtualized - 新規プロジェクトでは非推奨
// 既存プロジェクトの維持管理のみに使用を検討
import { List } from 'react-virtualized';

react-windowreact-virtualized の作者によって作成された後継ライブラリで、活発にメンテナンスされています。

// ✅ react-window - 新規プロジェクトで推奨
import { FixedSizeList } from 'react-window';

react-infinite-scroll-componentreact-tiny-virtual-list は継続してメンテナンスされていますが、使用ユースケースが限定的です。

📊 比較サマリー

機能react-infinite-scroll-componentreact-tiny-virtual-listreact-virtualizedreact-window
仮想化❌ なし✅ あり✅ あり✅ あり
無限スクロール✅ 内蔵❌ 別途実装❌ 別途実装❌ 別途実装
可変高さ✅ 対応❌ 固定のみ✅ 対応✅ 対応
提供コンポーネント1 種1 種多数多数
メンテナンス状況✅ 継続中✅ 継続中⚠️ メンテナンスモード✅ 継続中
新規プロジェクト⚠️ 限定的⚠️ 限定的❌ 非推奨✅ 推奨

💡 実装ガイドライン

100 件以下のリスト

アイテム数が少ない場合、仮想化のオーバーヘッドが不要なことがあります。

// 単純な map で十分
{items.slice(0, 100).map(item => (
  <div key={item.id}>{item.name}</div>
))}

100〜1000 件のリスト、無限スクロールが必要

react-infinite-scroll-component が適しています。

import InfiniteScroll from 'react-infinite-scroll-component';

<InfiniteScroll
  dataLength={items.length}
  next={fetchMore}
  hasMore={hasMore}
>
  {items.map(item => <Item key={item.id} data={item} />)}
</InfiniteScroll>

1000 件以上のリスト、固定高さアイテム

react-windowFixedSizeList が最適です。

import { FixedSizeList } from 'react-window';

<FixedSizeList
  height={600}
  itemCount={items.length}
  itemSize={50}
>
  {({ index, style }) => (
    <div style={style}>{items[index].name}</div>
  )}
</FixedSizeList>

1000 件以上のリスト、可変高さアイテム

react-windowVariableSizeList が適しています。

import { VariableSizeList } from 'react-window';

const listRef = useRef();

<VariableSizeList
  ref={listRef}
  height={600}
  itemCount={items.length}
  itemSize={index => getItemHeight(items[index])}
>
  {({ index, style }) => (
    <div style={style}>{items[index].content}</div>
  )}
</VariableSizeList>

グリッドレイアウトが必要

react-windowFixedSizeGrid または VariableSizeGrid を使用します。

import { FixedSizeGrid } from 'react-window';

<FixedSizeGrid
  columnCount={10}
  columnWidth={100}
  rowCount={100}
  rowHeight={50}
  width={1000}
  height={500}
>
  {({ columnIndex, rowIndex, style }) => (
    <div style={style}>
      {items[rowIndex * 10 + columnIndex]}
    </div>
  )}
</FixedSizeGrid>

🎯 最終推奨

新規プロジェクトでは、react-window を第一選択肢としてください。モダンな設計、活発なメンテナンス、優れたパフォーマンスを備えています。

無限スクロールのみが必要で、アイテム数が少ない場合は、react-infinite-scroll-component の使用を検討できますが、将来的なパフォーマンス問題に注意してください。

既存の react-virtualized プロジェクトは、段階的に react-window への移行を計画してください。両ライブラリの API は似ているため、移行コストは比較的低く抑えられます。

最小限の依存関係が求められるシンプルなユースケースでは、react-tiny-virtual-list が有効ですが、機能制限を理解した上で使用してください。

選び方: react-infinite-scroll-component vs react-tiny-virtual-list vs react-virtualized vs react-window

  • react-infinite-scroll-component:

    react-infinite-scroll-component は、アイテム数が数百件程度で、シンプルな無限スクロール実装が必要な場合に適しています。仮想化機能がないため、数千件以上のアイテムを扱うプロジェクトではパフォーマンス問題が発生する可能性があります。既存のコードベースで既に導入されており、パフォーマンス要件が低い場合に限り選択を検討してください。

  • react-tiny-virtual-list:

    react-tiny-virtual-list は、固定高さのアイテムを扱うシンプルで軽量な仮想リストが必要な場合に適しています。依存関係を最小限に抑えたいプロジェクトや、カスタマイズが最小限で済むユースケースに向いています。可変高さのアイテムや複雑なレイアウトが必要な場合は、他のライブラリを検討してください。

  • react-virtualized:

    react-virtualized は、現在はメンテナンスモードのため、新しいプロジェクトでの使用は推奨されません。既存のレガシープロジェクトで既に導入されている場合の維持管理に限って検討してください。新規プロジェクトでは、後継である react-window の使用を強く推奨します。

  • react-window:

    react-window は、新しいプロジェクトで仮想リストやグリッドを実装する際の第一選択肢です。モダンな設計、軽量な実装、活発なメンテナンスが特徴です。可変高さのアイテムや複雑なレイアウトにも対応しており、パフォーマンスと機能性のバランスが優れています。

react-infinite-scroll-component のREADME

react-infinite-scroll-component npm npm

All Contributors

A component to make all your infinite scrolling woes go away with just 4.15 kB! Pull Down to Refresh feature added. An infinite-scroll that actually works and super-simple to integrate!

Install

  npm install --save react-infinite-scroll-component

  or

  yarn add react-infinite-scroll-component

  // in code ES6
  import InfiniteScroll from 'react-infinite-scroll-component';
  // or commonjs
  var InfiniteScroll = require('react-infinite-scroll-component');

Using

<InfiniteScroll
  dataLength={items.length} //This is important field to render the next data
  next={fetchData}
  hasMore={true}
  loader={<h4>Loading...</h4>}
  endMessage={
    <p style={{ textAlign: 'center' }}>
      <b>Yay! You have seen it all</b>
    </p>
  }
  // below props only if you need pull down functionality
  refreshFunction={this.refresh}
  pullDownToRefresh
  pullDownToRefreshThreshold={50}
  pullDownToRefreshContent={
    <h3 style={{ textAlign: 'center' }}>&#8595; Pull down to refresh</h3>
  }
  releaseToRefreshContent={
    <h3 style={{ textAlign: 'center' }}>&#8593; Release to refresh</h3>
  }
>
  {items}
</InfiniteScroll>

Using scroll on top

<div
  id="scrollableDiv"
  style={{
    height: 300,
    overflow: 'auto',
    display: 'flex',
    flexDirection: 'column-reverse',
  }}
>
  {/*Put the scroll bar always on the bottom*/}
  <InfiniteScroll
    dataLength={this.state.items.length}
    next={this.fetchMoreData}
    style={{ display: 'flex', flexDirection: 'column-reverse' }} //To put endMessage and loader to the top.
    inverse={true} //
    hasMore={true}
    loader={<h4>Loading...</h4>}
    scrollableTarget="scrollableDiv"
  >
    {this.state.items.map((_, index) => (
      <div style={style} key={index}>
        div - #{index}
      </div>
    ))}
  </InfiniteScroll>
</div>

The InfiniteScroll component can be used in three ways.

  • Specify a value for the height prop if you want your scrollable content to have a specific height, providing scrollbars for scrolling your content and fetching more data.
  • If your scrollable content is being rendered within a parent element that is already providing overflow scrollbars, you can set the scrollableTarget prop to reference the DOM element and use it's scrollbars for fetching more data.
  • Without setting either the height or scrollableTarget props, the scroll will happen at document.body like Facebook's timeline scroll.

docs version wise

3.0.2

live examples

  • infinite scroll (never ending) example using react (body/window scroll)
    • Edit yk7637p62z
  • infinte scroll till 500 elements (body/window scroll)
    • Edit 439v8rmqm0
  • infinite scroll in an element (div of height 400px)
    • Edit w3w89k7x8
  • infinite scroll with scrollableTarget (a parent element which is scrollable)
    • Edit r7rp40n0zm

props

nametypedescription
nextfunctiona function which must be called after reaching the bottom. It must trigger some sort of action which fetches the next data. The data is passed as children to the InfiniteScroll component and the data should contain previous items too. e.g. Initial data = [1, 2, 3] and then next load of data should be [1, 2, 3, 4, 5, 6].
hasMorebooleanit tells the InfiniteScroll component on whether to call next function on reaching the bottom and shows an endMessage to the user
childrennode (list)the data items which you need to scroll.
dataLengthnumberset the length of the data.This will unlock the subsequent calls to next.
loadernodeyou can send a loader component to show while the component waits for the next load of data. e.g. <h3>Loading...</h3> or any fancy loader element
scrollThresholdnumber | stringA threshold value defining when InfiniteScroll will call next. Default value is 0.8. It means the next will be called when user comes below 80% of the total height. If you pass threshold in pixels (scrollThreshold="200px"), next will be called once you scroll at least (100% - scrollThreshold) pixels down.
onScrollfunctiona function that will listen to the scroll event on the scrolling container. Note that the scroll event is throttled, so you may not receive as many events as you would expect.
endMessagenodethis message is shown to the user when he has seen all the records which means he's at the bottom and hasMore is false
classNamestringadd any custom class you want
styleobjectany style which you want to override
heightnumberoptional, give only if you want to have a fixed height scrolling content
scrollableTargetnode or stringoptional, reference to a (parent) DOM element that is already providing overflow scrollbars to the InfiniteScroll component. You should provide the id of the DOM node preferably.
hasChildrenboolchildren is by default assumed to be of type array and it's length is used to determine if loader needs to be shown or not, if your children is not an array, specify this prop to tell if your items are 0 or more.
pullDownToRefreshboolto enable Pull Down to Refresh feature
pullDownToRefreshContentnodeany JSX that you want to show the user, default={<h3>Pull down to refresh</h3>}
releaseToRefreshContentnodeany JSX that you want to show the user, default={<h3>Release to refresh</h3>}
pullDownToRefreshThresholdnumberminimum distance the user needs to pull down to trigger the refresh, default=100px , a lower value may be needed to trigger the refresh depending your users browser.
refreshFunctionfunctionthis function will be called, it should return the fresh data that you want to show the user
initialScrollYnumberset a scroll y position for the component to render with.
inverseboolset infinite scroll on top

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Ankeet Maini

💬 📖 💻 👀 🚧

Darsh Shah

🚇

This project follows the all-contributors specification. Contributions of any kind are welcome!

LICENSE

MIT