Implementing Fixed, Variable, and Dynamic Height Virtual Lists with React‑Window
This article explains how to build three kinds of virtual scrolling lists—fixed‑height, variable‑height, and dynamic‑height—using React‑Window, covering core principles, detailed implementation code, performance considerations, and practical tips for handling irregular item sizes.
Introduction
While working with large data sets in React, virtual scrolling is essential for performance. Using react-window as a base, this guide demonstrates three virtual list patterns: fixed item height, variable item height, and dynamic item height.
Virtual List Core Principle
The list is divided into three zones: an upper buffer, the visible window, and a lower buffer. When an item leaves the visible area, it is removed from the upper buffer and a new item is added to the lower buffer, keeping the total number of rendered elements constant.
Implementation
1. Fixed‑Height Virtual List
Usage
const Row = ({ index, style, forwardRef }) => {
return (
{`Row ${index}`}
);
};
const App = () => {
return (
{Row}
);
};Implementation Details
1) Compute the total container height for 1000 items. 2) Derive the start index of the visible area, the start index of the upper buffer, and the end index of the lower buffer. 3) Position each rendered item absolutely using its calculated top value.
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 item has a random height, the total container height cannot be computed directly. Instead, the algorithm estimates a sufficiently large container and calculates the top of each rendered item by accumulating the heights of preceding items.
Key Challenges and Solutions
Unknown total height – use an over‑estimated container.
Cannot compute top via index * itemSize – accumulate actual heights.
Scroll offset does not map to an index directly – iterate forward from the last measured item until the accumulated offset exceeds the scroll offset.
3. Dynamic‑Height Virtual List
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}
);In this variant the itemSize prop is omitted; the list learns each item's height after it is rendered. A ResizeObserver watches size changes and updates the cached metadata, then forces a re‑render.
Supporting Components
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 implementation combines the metadata helpers ( estimatedHeight , getItemMetaData , getStartIndex , getEndIndex , getRangeToRender ) with the ListItem component to achieve smooth scrolling even when items resize dynamically.
Conclusion
React‑Window only provides fixed‑height and variable‑height lists; the dynamic‑height version is built by extending the variable‑height logic and adding resize observation. Understanding the buffer zones and offset calculations allows developers to hand‑craft efficient virtual lists for any item‑size pattern.
For the complete source code, visit the repository at https://gitee.com/zhutouqietuzai/virtual-list .
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.