Handling 100,000 Rows of Data Smoothly in React with react-window

Development tutorial - IT technology blog
Development tutorial - IT technology blog

When Data Lists Become a Browser’s Nightmare

Have you ever tried rendering a table or list with about 10,000 rows and watched Chrome start to freeze? It’s a terrible experience. Scrolling becomes laggy, and clicking an item takes a full second for the browser to respond.

The real problem isn’t React itself. The main culprit is how the browser handles the DOM. Think about it: if you render 10,000 rows, each containing 5 HTML tags, the browser has to manage 50,000 DOM nodes. With just a small state change, the browser must recalculate the layout (reflow) and repaint that massive pile of nodes, causing the FPS to tank.

I once worked on a dashboard displaying real-time server logs. Initially, the team used .map() to display the data, thinking a few thousand rows wouldn’t be an issue. As it turned out, after 30 minutes of operation, the log count spiked, Chrome’s RAM usage hit nearly 2GB, and the tab crashed completely. The lesson is clear: never force the browser to render things the user can’t see.

Virtualization Technique: Only Render What You See

The principle of Virtualization (or Windowing) is simple. Instead of building all 10,000 rows, we only render the specific rows within the viewport, plus a few buffer rows to ensure smooth scrolling.

As you scroll down, old elements moving out of view are immediately removed from the DOM. Simultaneously, new elements are inserted. This keeps the number of DOM nodes at a minimum—usually around 20-30 nodes—regardless of how large your dataset is.

In the React community, react-window is the gold standard for this. It’s a lightweight version of react-virtualized, focused entirely on performance and incredibly easy to set up.

Hands-on: Integrating react-window into Your Project

First, add the library to your project:

npm install react-window
# Or
yarn add react-window

1. Using FixedSizeList for Fixed Heights

If the rows in your list have a uniform height (e.g., 50px), this is the most optimized choice.

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style, data }) => (
  // Style is crucial for positioning the rows
  <div style={style} className="border-b flex items-center px-4">
    <span>Row {index + 1}: {data[index].name}</span>
  </div>
);

const MyList = ({ items }) => (
  <List
    height={500}        // Viewport height (px)
    itemCount={items.length} 
    itemSize={50}       // Row height (px)
    width="100%"        
    itemData={items}    
  >
    {Row}
  </List>
);

Quick note: the style prop in the Row component is mandatory. react-window uses position: absolute to place rows correctly as you scroll. Without it, all rows will stack on top of each other at the top.

2. Handling Dynamic Heights (VariableSizeList)

If the content length varies per row, use VariableSizeList. In this case, itemSize accepts a function to calculate dimensions based on the index.

import { VariableSizeList as List } from 'react-window';

const getItemSize = index => (index % 2 === 0 ? 50 : 100);

const MyVariableList = ({ items }) => (
  <List
    height={500}
    itemCount={items.length}
    itemSize={getItemSize}
    width="100%"
  >
    {({ index, style }) => (
      <div style={style}>Row {index} with custom height</div>
    )}
  </List>
);

Practical Performance Optimization Tips

To achieve a smooth 60 FPS while scrolling, keep these two critical tips in mind:

Use React.memo for the Row Component

Even though Virtualization reduces the node count, scrolling can still stutter if the Row component performs heavy calculations. Wrap Row in React.memo along with the areEqual helper from the library to prevent unnecessary re-renders.

Auto-sizing with AutoSizer

react-window requires specific numerical values for height and width. However, web interfaces usually need to be responsive. You should use react-virtualized-auto-sizer to let the list automatically fill its parent container.

When Should You Avoid react-window?

Despite its power, you should avoid overusing it in the following cases:

  • Short lists under 200 items: The DOM handles these well; using .map() will keep your code cleaner.
  • Native search functionality (Ctrl + F): Since off-screen rows aren’t in the DOM, the browser won’t find them.
  • Highly complex layouts that don’t follow a row or grid structure.

Summary

React optimization isn’t just about useMemo. Changing how you interact with the DOM is the real key to handling big data. With react-window, you can process tens of thousands of rows while keeping RAM usage low and the browser smooth. Try it in your project today to see the difference in responsiveness!

Share: