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.

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

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 (
    <div className={index % 2 ? 'list-item-odd' : 'list-item-even'} style={style} ref={forwardRef}>
      {`Row ${index}`}
    </div>
  );
};

const App = () => {
  return (
    <FixedSizeList className="list" height={200} width={200} itemSize={50} itemCount={1000}>
      {Row}
    </FixedSizeList>
  );
};

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 }) => (
  <div className={index % 2 ? 'list-item-odd' : 'list-item-even'} style={style}>
    Row {index}
  </div>
);

const App = () => (
  <VariableSizeList className="list" height={200} width={200} itemSize={getItemSize} itemCount={1000}>
    {Row}
  </VariableSizeList>
);

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(
    <div className={i % 2 ? 'list-item-odd' : 'list-item-even'} style={style}>Row {i}</div>
  );
}

const Row = ({ index }) => items[index];

const App = () => (
  <VariableSizeList className="list" height={200} width={200} itemCount={itemCount} isDynamic>
    {Row}
  </VariableSizeList>
);

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 (
      <div style={style} ref={this.domRef}>
        <ComponentType index={index} />
      </div>
    );
  }
}

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 .

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendperformanceReactvirtual-list
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

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.