はじめに
数千件、数万件のデータをテーブルやリストで表示しようとしたら、スクロールがカクついて固まってしまった――そんな経験はないでしょうか。
原因のほとんどは「見えていない行までDOMに描画してしまっている」ことにあります。1万件のリストを愚直にmapで描画すれば、ブラウザは1万個のDOM要素を保持し続けることになり、レンダリングもメモリも重くなる一方です。
React-Windowは、この問題を「画面に見えている範囲だけを描画する」という発想で解決するライブラリです。React DevToolsやReplayブラウザといったプロダクトでも採用されており、軽量かつシンプルなAPIで大量データの描画を高速化できます。
React-Windowとは
React-Windowは、Reactの作者の一人であるBrian Vaughn氏が開発している仮想スクロール(virtualization)用のコンポーネントライブラリです。リストやテーブルなど、大量の項目を持つUIにおいて、実際にビューポート内に表示される行・セルのみをDOMに描画することで、パフォーマンスを大幅に改善します。
主な特徴
- 仮想スクロール(Windowing) - スクロール位置に応じて表示範囲のみをレンダリングし、DOM要素数を最小限に抑えます
- 軽量な設計 - 依存ライブラリを持たず、パッケージサイズも小さく保たれています
- List・Gridの両対応 - 一次元のリストだけでなく、行と列を持つグリッド(テーブル)状のデータにも対応します
- 可変サイズ対応 - 行の高さや列の幅が一定でないケースにも、動的な計測フックを使って対応できます
- TypeScript対応 - 型定義が同梱されており、
RowComponentPropsなど型安全にコンポーネントを実装できます
なお、2026年時点の最新バージョン(2.x系)ではAPIが刷新されており、以前のバージョンで使われていたFixedSizeListやVariableSizeListといった個別コンポーネントは、List・Gridという2つのコンポーネントに統合されています。本記事では最新のAPIをベースに解説します。
インストール
npm、yarn、pnpmのいずれでもインストール可能です。
# npm
npm install react-window
# yarn
yarn add react-window
# pnpm
pnpm add react-window
対応するReactのバージョンは18系・19系です。TypeScriptの型定義も同梱されているため、追加で@typesパッケージを入れる必要はありません。
基本的な使い方
リストを表示する
まずは、名前の配列をスクロール可能なリストとして表示する例です。行を描画するコンポーネント(RowComponent)を定義し、Listコンポーネントに渡すだけで実装できます。
// RowComponent.tsx
import { type RowComponentProps } from "react-window";
type RowProps = {
names: string[];
};
function RowComponent({ index, names, style }: RowComponentProps<RowProps>) {
return (
<div style={style} className="flex items-center justify-between">
{names[index]}
<span className="text-xs text-slate-500">
{index + 1} / {names.length}
</span>
</div>
);
}
export { RowComponent };
// NameList.tsx
import { List } from "react-window";
import { RowComponent } from "./RowComponent";
function NameList({ names }: { names: string[] }) {
return (
<List
rowComponent={RowComponent}
rowCount={names.length}
rowHeight={25}
rowProps={{ names }}
/>
);
}
export { NameList };
ポイントは次の4つのpropsです。
rowComponent- 各行を描画するコンポーネントrowCount- 描画する行の総数rowHeight- 1行あたりの高さ(px指定のほか、関数での動的指定も可能)rowProps- 行コンポーネントに渡したい追加のprops
行コンポーネント側はindexとstyleを受け取りますが、styleは仮想スクロールの位置計算に使われるため、必ずルート要素にそのまま渡す必要があります。
グリッド(テーブル)を表示する
行と列の両方を持つデータにはGridコンポーネントを使います。
// CellComponent.tsx
import { type CellComponentProps } from "react-window";
type Contact = { name: string; email: string; phone: string };
function CellComponent({
contacts,
columnIndex,
rowIndex,
style
}: CellComponentProps<{ contacts: Contact[] }>) {
const contact = contacts[rowIndex];
const columns: (keyof Contact)[] = ["name", "email", "phone"];
const content = contact[columns[columnIndex]];
return (
<div style={style} className="truncate border-b px-2">
{content}
</div>
);
}
export { CellComponent };
// ContactGrid.tsx
import { Grid } from "react-window";
import { CellComponent } from "./CellComponent";
function ContactGrid({ contacts }: { contacts: { name: string; email: string; phone: string }[] }) {
return (
<Grid
cellComponent={CellComponent}
cellProps={{ contacts }}
columnCount={3}
columnWidth={200}
rowCount={contacts.length}
rowHeight={32}
/>
);
}
export { ContactGrid };
Listと同様に、cellComponent・columnCount・columnWidth・rowCount・rowHeight・cellPropsを指定するだけで、大量の連絡先データを持つテーブルも軽快に描画できます。
実践的なユースケース
行の高さが不揃いなチャットログを表示する
チャットのメッセージ一覧のように、テキスト量によって行の高さが変わる場合は、useDynamicRowHeightフックを使うことで、実際に描画された要素の高さを自動計測しながらリストを組み立てられます。
import { List, useDynamicRowHeight, type RowComponentProps } from "react-window";
type MessageRowProps = {
messages: string[];
};
function MessageRow({ index, messages, style }: RowComponentProps<MessageRowProps>) {
return (
<div style={style} className="whitespace-pre-wrap px-2 py-1">
{messages[index]}
</div>
);
}
function ChatLog({ messages }: { messages: string[] }) {
const rowHeight = useDynamicRowHeight({ defaultRowHeight: 50 });
return (
<List
rowComponent={MessageRow}
rowCount={messages.length}
rowHeight={rowHeight}
rowProps={{ messages }}
/>
);
}
export { ChatLog };
行の内容が展開・折りたたみで変化するようなUI(FAQ一覧やアコーディオンリストなど)にも同じ仕組みが応用できます。
大規模な管理画面のテーブル
数万件規模の在庫データや注文履歴を一覧表示する管理画面では、Gridを使うことで、行・列どちらの方向にもスクロール可能なテーブルを、ページネーションなしで実装できます。実データをすべてメモリ上に保持しつつ、画面に描画するのは可視範囲のセルだけなので、ブラウザの描画負荷を抑えながらスムーズなスクロール体験を提供できます。
まとめ
React-Windowは、大量データを扱うリストやテーブルの描画パフォーマンスを、シンプルなAPIで解決してくれるライブラリです。
rowComponent/cellComponentとサイズ指定だけで仮想スクロールを実現できるList・Gridという2つのコンポーネントで、一次元・二次元どちらのデータにも対応できるuseDynamicRowHeightのようなフックで、可変サイズの行にも柔軟に対応できる
「リストの件数が増えるたびに画面が重くなる」と感じたら、まずは既存のリストをReact-Windowに置き換えてみることをおすすめします。少ない変更量で、体感できるほどのパフォーマンス改善が得られるはずです。