Frontend Development 17 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing Fixed, Variable, and Dynamic Height Virtual Lists with react-window

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

FrontendperformanceReactvirtual-listreact-window
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.