Library / SDK
bvaughn/react-window avatar
bvaughn/react-window

react-window: virtualized lists for React, and where the approach breaks down

React components for efficiently rendering large lists and tabular data

17,208 stars817 forksTypeScriptMIT

At a glance

What is it?
react-window renders only the rows a user can see, which is how it keeps long lists responsive. Here is the mechanism, the install path, and the cases where it is the wrong component.
Who is it for?
Adopt react-window when you have a long, mostly uniform list or grid and you control the row markup: install it with pnpm add react-window, render a row component through the list component, and give the container a real height. Do not adopt it when you need variable row heights with automatic measurement, or when your rows are heavy components that must keep their own state across scroll positions, because the library reuses a small window of row instances.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem react-window solves: too many DOM nodes, not too much data

Rendering ten thousand rows in React means ten thousand component instances and ten thousand DOM nodes, even though the browser shows perhaps twenty of them at once. The cost lands on mount time, on memory, and on every layout pass. react-window takes the position that the data is fine and the rendering is the problem: it renders a small window of rows around the visible area and swaps their contents as the user scrolls, so the node count stays roughly constant while the list length grows.

The README states the goal plainly, describing the library as one that "helps render large lists of data quickly and without the performance problems that often go along with rendering a lot of data." It also names two production consumers, React DevTools and the Replay browser. That matters for judging the project: devtools panels are exactly the workload where a list is long, rows are uniform, and a dropped frame is visible to the user.

The audience is React application developers who own the rendering layer. If your rows come from a design system you cannot control, or your list lives inside a component that already manages its own scrolling, the fit is worse than the README suggests.

How the windowing mechanism actually works

The core idea is a viewport-sized window over an index range. The list component measures its own container, divides the scroll offset by the row height to find the first visible index, adds the number of rows that fit in the container, and renders that slice. Everything outside the slice is represented by blank space, usually two spacer elements whose heights account for the rows above and below the window. The scrollbar therefore reflects the full list length while the DOM holds only the visible slice.

This is why the fixed-size variants are cheap: if every row is the same height, the mapping from scroll offset to index is a division, and no measurement is needed. Variable-size variants trade that arithmetic for a lookup: the library needs to know the offset of each row, which means either a height you provide per index or a measurement pass after render. That measurement pass is where most of the practical friction lives, because a row whose height changes after mount invalidates the offsets computed before it.

The repository layout reflects a library that is also its own documentation site. The top level holds src/, lib/, integrations/, and a Vite configuration, with scripts for compiling docs, examples, and a search index. Integration directories exist for Next, Vike, and Vite, which is a useful signal about the environments the maintainers actually exercise: framework-level SSR setups, not just a bare client bundle. The package ships as ESM with a CJS build and separate type declarations, so it drops into both modern bundlers and older CommonJS consumers.

Installing react-window and rendering a first list

The package is published as react-window on npm and the repository is a pnpm workspace, so pnpm is the install path the maintainers use themselves. Add it to an existing React project:

bash
pnpm add react-window

Your package manager will resolve the current 2.x line. The package.json declares "type": "module" and exposes dist/react-window.js as the module entry, dist/react-window.cjs for CommonJS, and dist/react-window.d.ts for types, so TypeScript users get declarations without an extra @types package.

A minimal list needs a container with a definite height. The library cannot compute a window against a container whose height is determined by its own content, which is a common first-run failure: the list renders nothing or collapses to zero height. Give the wrapper an explicit height in CSS or inline style, then render the list inside it. The exact component and prop names differ between the 1.x and 2.x APIs, so check the documentation site at react-window.vercel.app for the current names before copying an older example from a blog post. The shape of the code is a list component receiving a row count, a row height, and a row renderer that receives an index and style, with the style applied to the row element so the library can position it.

If you want to see the intended behaviour before wiring anything, the repository has a dev script that runs the Vite dev server, and the docs site is the same codebase, so the examples on the site are the examples in the repo.

Where react-window is the wrong tool

The library's model assumes rows can be produced from an index. That assumption fails in several common situations, and the documentation does not paper over them.

First, rows with unpredictable heights. If a row contains user-generated text of unknown length, an image that loads late, or a nested expandable section, the height is not known at the moment the window is computed. Variable-size support exists, but it requires you to supply or measure heights, and any measurement that arrives after the offsets were calculated forces a recalculation. Lists that mix fixed and dynamic heights are the worst case.

Second, rows that hold meaningful local state. Because the library reuses a small set of row instances and changes their contents, a row component that keeps state (an open dropdown, a half-typed input, a video position) can have that state applied to a different data item after scrolling. The standard fix is to lift state out of the row and key it by item id, which is a real refactor, not a one-line change.

Third, accessibility and find-in-page. Content that is not mounted cannot be found by the browser's search, and screen reader behaviour over a virtualized list depends on the ARIA roles you apply to the container and rows. The README does not document an accessibility contract, so treat this as work you own. If your list is short (a few hundred rows) or your rows are cheap, plain rendering plus pagination is simpler and has none of these failure modes.

react-window compared with react-virtualized and TanStack Virtual

react-virtualized is the older library from the same author, and the search data shows people comparing the two directly. The difference is scope: react-virtualized bundles a wider set of components, including grid, table, masonry, and collection variants, in one package, while react-window narrows to list and grid rendering. Narrower means less API surface to learn and a smaller dependency, at the cost of features you may have to build yourself. If you are starting fresh, the narrower library is usually the easier one to reason about; if you already depend on react-virtualized's table or masonry components, migrating is a rewrite of the rendering layer.

TanStack Virtual takes a different architectural position: it is a headless virtualization primitive rather than a component library. It gives you the measurements and offsets and expects you to render the elements yourself, which means no imposed row markup and no imposed container. That is more flexible and more code. react-window makes the rendering decisions for you, which is faster to adopt and harder to bend when your layout does not match its assumptions. The same trade-off applies against react-virtuoso, which leans toward automatic measurement of variable-height content; if measurement is your main problem, that is the axis to compare on, not the API surface.

Maintenance, licence, and the cost of staying current

The repository is not archived, and the last push was on 2026-09-20. Releases are recent: 2.3.1 and 2.3.0 both landed on 2026-09-05, with 2.2.7 before them on 2026-02-13. The gap between 2.2.7 and 2.3.0 is roughly seven months, which suggests a project that ships in bursts rather than continuously, and 2.3.x arriving as a pair of releases on the same day is consistent with a fix landing right after a feature release.

The upgrade cost is concentrated in major versions. The 2.x line is a breaking change from 1.x, and the search data shows people searching for "react-window v2" and hitting "react window is undefined", which is the signature of an import that worked in 1.x and no longer resolves in 2.x. If you pin 1.x, you are on a branch that is no longer where new work lands. Budget the migration as a rendering-layer change, not a version bump, and check CHANGELOG.md in the repository for the specific removals that affect your call sites.

The licence is MIT, declared in package.json and shipped as LICENSE.md at the repository root. That permits commercial use, modification, and redistribution provided the copyright notice and permission notice are retained. It also means there is no warranty and no support obligation from the author, which is worth stating plainly for teams that treat a dependency as a vendor relationship. This is a description of the licence text, not legal advice; your own counsel decides what your distribution model requires.

Editorial conclusion

Adopt react-window when you have a long, mostly uniform list or grid and you control the row markup: install it with pnpm add react-window, render a row component through the list component, and give the container a real height. Do not adopt it when you need variable row heights with automatic measurement, or when your rows are heavy components that must keep their own state across scroll positions, because the library reuses a small window of row instances. Before committing, verify two things against the current documentation: the exact prop names for your installed version, since 2.x differs from the 1.x API, and whether your row content can be rendered from an index alone. If your rows need measurement or rich per-row state, look at react-virtuoso or TanStack Virtual instead.

Frequently asked questions

What is react-window used for?

It renders large lists of data quickly by avoiding the performance problems that come with rendering many rows at once. The README names React DevTools and the Replay browser as consumers.

What does react-window do?

It is a component library for efficiently rendering large lists and tabular data in React. Rather than mounting every row, it keeps the rendered set small and updates it as the user scrolls.

How do you use react-window?

Install it with pnpm add react-window, then render a list component inside a container that has a definite height, supplying the row count, the row height, and a row renderer that applies the style it receives. Prop and component names differ between the 1.x and 2.x APIs, so check the documentation site for the version you installed.

How does react-window compare with react-virtualized?

Both come from the same author, but react-virtualized ships a wider set of components such as grid, table, masonry, and collection, while react-window narrows to list and grid rendering. The narrower package means less API surface but also fewer built-in features.

What are react-window alternatives?

TanStack Virtual offers headless virtualization primitives and expects you to render the elements yourself, while react-virtuoso leans toward automatic measurement of variable-height content. react-window instead makes the rendering decisions for you.

Official sources

  1. bvaughn/react-window on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bvaughn-react-window.svg)](https://hysenlabs.com/projects/bvaughn-react-window)