Implementing Fixed, Variable, and Dynamic Height Virtual Lists with react-window
This article explains the core principles of virtual scrolling and provides complete React implementations for three types of virtual lists—fixed‑height, variable‑height, and dynamic‑height—using react-window, including detailed code snippets, performance considerations, and optimization suggestions.
Preface
In my daily work I frequently encounter large data‑rendering scenarios and have used react-window many times, which gave me a basic understanding of virtual lists. This article demonstrates three virtual‑list implementations based on react-window .
Core Principle of Virtual Lists
The list is divided into three zones: an upper buffer, a visible area, and a lower buffer. When an item leaves the visible area, the top buffer item is removed and a new item is added to the bottom buffer, keeping the total number of rendered DOM nodes small.
The static‑height example shows that only six items are initially rendered; after scrolling, eight items remain rendered, confirming the buffering strategy.
Implementation of Virtual Lists
1. Fixed‑Height Virtual List
Usage
const Row = ({ index, style, forwardRef }) => {
return (
{`Row ${index}`}
);
};
const App = () => {
return (
{Row}
);
};
export default FixedSizeList;Implementation Details
(1) Compute the total container height for 1000 items. (2) Determine the start index of the visible area and the start/end indices of the upper and lower buffers. (3) Use absolute positioning to calculate each item's top value within the container. (4) Render only the items that fall inside the three zones.
2. Variable‑Height Virtual List
Usage
const rowSizes = new Array(1000).fill(true).map(() => 25 + Math.round(Math.random() * 55));
const getItemSize = index => rowSizes[index];
const Row = ({ index, style }) => (
Row {index}
);
const App = () => (
{Row}
);Because each row has a random height, the total container height cannot be calculated simply as itemSize * index . The implementation records measured heights, estimates unmeasured items, and uses binary search‑like logic to find the start index based on the current scroll offset.
Key Challenges and Solutions
Challenge 1: Unknown total height
Instead of calculating an exact total height, a sufficiently large estimated container is created to enable scrolling.
Challenge 2 & 3: Unknown top values and start index
When scrolling, the algorithm keeps a map of measured items. For a new scroll position it walks forward from the last measured item, accumulating heights until the accumulated offset exceeds the scroll offset, thereby determining the visible start index and each item's top .
3. Dynamic‑Height Virtual List
This version builds on the variable‑height list but adds a ResizeObserver to each rendered row so that height changes are detected and the metadata map is updated in real time.
Usage
const items = [];
const itemCount = 1000;
for (let i = 0; i < itemCount; i++) {
const height = 30 + Math.floor(Math.random() * 30);
const style = { height, width: '100%' };
items.push(
Row {i}
);
}
const Row = ({ index }) => items[index];
const App = () => (
{Row}
);Implementation Highlights
The ListItem component creates a ResizeObserver on its child node and calls onSizeChange(index, domNode) whenever the size changes. The handler updates the stored height and recomputes offsets for all subsequent items, then forces a re‑render.
class ListItem extends React.Component {
constructor(props) {
super(props);
this.domRef = React.createRef();
this.resizeObserver = null;
}
componentDidMount() {
if (this.domRef.current) {
const domNode = this.domRef.current.firstChild;
const { index, onSizeChange } = this.props;
this.resizeObserver = new ResizeObserver(() => {
onSizeChange(index, domNode);
});
this.resizeObserver.observe(domNode);
}
}
componentWillUnmount() {
if (this.resizeObserver && this.domRef.current.firstChild) {
this.resizeObserver.unobserve(this.domRef.current.firstChild);
}
}
render() {
const { index, style, ComponentType } = this.props;
return (
);
}
}The full VariableSizeList component now accepts an onSizeChange callback, updates the metadata map, and re‑calculates offsets whenever an item’s height changes.
Conclusion
Although react-window only provides fixed‑height and variable‑height lists, the dynamic‑height implementation can be built on top of it by observing size changes. Mastering the buffering principle makes hand‑crafting virtual lists straightforward, and the article invites readers to suggest further optimizations, especially for the variable‑height version.
Repository address: click here
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.