GitHub – bvaughn/react-window: React components for efficiently rendering large lists and tabular data

React Window Examples

react-window is a set of React components for efficiently rendering large lists and tabular data

React window works by only rendering part of a large data set (just enough to fill the viewport). This helps address some common performance bottlenecks:

  1. It reduces the amount of work (and time) required to render the initial view and to process updates.
  2. It reduces the memory footprint by avoiding over-allocation of DOM nodes.

Step 1: Install it

Install it using the following commands:

# Yarn
yarn add react-window

# NPM
npm install --save react-window

Step 2: Write Code Usage

Here are some basic examples:

Fixed Size List

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

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);
Column 0Column 1Column 2Column 3Column 4Column 5
Try it on CodeSandbox
import { FixedSizeList as List } from 'react-window';

const Column = ({ index, style }) => (
  <div style={style}>Column {index}</div>
);

const Example = () => (
  <List
    height={75}
    itemCount={1000}
    itemSize={100}
    layout="horizontal"
    width={300}
  >
    {Column}
  </List>
);

Variable Sized List

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

// These row heights are arbitrary.
// Yours should be based on the content of the row.
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));

const getItemSize = index => rowHeights[index];

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={getItemSize}
    width={300}
  >
    {Row}
  </List>
);
Column 0Column 1Column 2Column 3Column 4
Try it on CodeSandbox
import { VariableSizeList as List } from 'react-window';

// These column widths are arbitrary.
// Yours should be based on the content of the column.
const columnWidths = new Array(1000)
  .fill(true)
  .map(() => 75 + Math.round(Math.random() * 50));

const getItemSize = index => columnWidths[index];

const Column = ({ index, style }) => (
  <div style={style}>Column {index}</div>
);

const Example = () => (
  <List
    height={75}
    itemCount={1000}
    itemSize={getItemSize}
    layout="horizontal"
    width={300}
  >
    {Column}
  </List>
);

Fixed Size Grid

import { FixedSizeGrid as Grid } from 'react-window';

const Cell = ({ columnIndex, rowIndex, style }) => (
  <div style={style}>
    Item {rowIndex},{columnIndex}
  </div>
);

const Example = () => (
  <Grid
    columnCount={1000}
    columnWidth={100}
    height={150}
    rowCount={1000}
    rowHeight={35}
    width={300}
  >
    {Cell}
  </Grid>
);

Variable Sized Grid

import { VariableSizeGrid as Grid } from 'react-window';

// These item sizes are arbitrary.
// Yours should be based on the content of the item.
const columnWidths = new Array(1000)
  .fill(true)
  .map(() => 75 + Math.round(Math.random() * 50));
const rowHeights = new Array(1000)
  .fill(true)
  .map(() => 25 + Math.round(Math.random() * 50));

const Cell = ({ columnIndex, rowIndex, style }) => (
  <div style={style}>
    Item {rowIndex},{columnIndex}
  </div>
);

const Example = () => (
  <Grid
    columnCount={1000}
    columnWidth={index => columnWidths[index]}
    height={150}
    rowCount={1000}
    rowHeight={index => rowHeights[index]}
    width={300}
  >
    {Cell}
  </Grid>

Scrolling Indicators

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

const Row = ({ index, isScrolling, style }) => (
  <div style={style}>
    {isScrolling ? 'Scrolling' : <code>Row ${index}</code>}
  </div>
);

// If your component's items are expensive to render,
// You can boost performance by rendering a placeholder while the user is scrolling.
// To do this, add the <code>useIsScrolling</code> property to your List or Grid.
// Now an additional parameter, <code>isScrolling</code>, will be passed to your render method:
const Example = props => (
  <List useIsScrolling {...props}>
    {Row}
  </List>
);

Scrolling To an Item

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

const listRef = React.createRef();

// You can programatically scroll to a item within a List.
// First, attach a ref to the List:
<List ref={listRef} {...props} />

// Then call the scrollToItem() API method with an item index:
listRef.current.scrollToItem(200);

// The List will scroll as little as possible to ensure the item is visible.
// You can also specify a custom alignment: center, start, or end.
// For example:
listRef.current.scrollToItem(300, "center");
Scroll to row 100, column 50 (align: auto)
Scroll to row 300, column 150 (align: start)
Scroll to row 350, column 200 (align: end)
Scroll to row 200, column 100 (align: center)
Scroll to row 250, column 150 (align: smart)
Scroll to row 100 (align: auto)
Scroll to column 50 (align: auto)
Item 0,0Item 0,1Item 0,2Item 0,3Item 0,4Item 1,0Item 1,1Item 1,2Item 1,3Item 1,4Item 2,0Item 2,1Item 2,2Item 2,3Item 2,4Item 3,0Item 3,1Item 3,2Item 3,3Item 3,4
Try it on CodeSandbox
import { FixedSizeGrid as Grid } from 'react-window';

const gridRef = React.createRef();

// You can programatically scroll to a item within a Grid.
// First, attach a ref to the Grid:
<Grid ref={gridRef} {...props} />

// Then call the scrollToItem() API method with the item indices:
gridRef.current.scrollToItem({
  columnIndex: 50,
  rowIndex: 100
});

// The Grid will scroll as little as possible to ensure the item is visible.
// You can also specify a custom alignment: center, start, or end.
// For example:
gridRef.current.scrollToItem({
  align: "start",
  columnIndex: 150,
  rowIndex: 300
});

// You can specify only columnIndex or rowIndex if you just want to scroll one axis.
// For example:
gridRef.current.scrollToItem({
  columnIndex: 100,
});

Memoized List Items

import React, { memo } from 'react';
import memoize from 'memoize-one';
import { FixedSizeList as List, areEqual } from 'react-window';

// If list items are expensive to render,
// Consider using React.memo or shouldComponentUpdate to avoid unnecessary re-renders.
// https://reactjs.org/docs/react-api.html#reactmemo
// https://reactjs.org/docs/react-api.html#reactpurecomponent
const Row = memo(({ data, index, style }) => {

  // Data passed to List as "itemData" is available as props.data
  const { items, toggleItemActive } = data;
  const item = items[index];

  return (
    <div
      onClick={() => toggleItemActive(index)}
      style={style}
    >
      {item.label} is {item.isActive ? 'active' : 'inactive'}
    </div>
  );
}, areEqual);

// This helper function memoizes incoming props,
// To avoid causing unnecessary re-renders pure Row components.
// This is only needed since we are passing multiple props with a wrapper object.
// If we were only passing a single, stable value (e.g. items),
// We could just pass the value directly.
const createItemData = memoize((items, toggleItemActive) => ({
  items,
  toggleItemActive,
}));

// In this example, "items" is an Array of objects to render,
// and "toggleItemActive" is a function that updates an item's state.
function Example({ height, items, toggleItemActive, width }) {

  // Bundle additional data to list items using the "itemData" prop.
  // It will be accessible to item renderers as props.data.
  // Memoize this data to avoid bypassing shouldComponentUpdate().
  const itemData = createItemData(items, toggleItemActive);

  return (
    <List
      height={height}
      itemCount={items.length}
      itemData={itemData}
      itemSize={35}
      width={width}
    >
      {Row}
    </List>
  );
}

Learn more at react-window.now.sh:

Related libraries

  • react-virtualized-auto-sizer: HOC that grows to fit all of the available space and passes the width and height values to its child.
  • react-window-infinite-loader: Helps break large data sets down into chunks that can be just-in-time loaded as they are scrolled into view. It can also be used to create infinite loading lists (e.g. Facebook or Twitter).
  • react-vtree: Lightweight and flexible solution to render large tree structures (e.g., file system).

Frequently asked questions

How is react-window different from react-virtualized?

I wrote react-virtualized several years ago. At the time, I was new to both React and the concept of windowing. Because of this, I made a few API decisions that I later came to regret. One of these was adding too many non-essential features and components. Once you add something to an open source project, removing it is pretty painful for users.

react-window is a complete rewrite of react-virtualized. I didn't try to solve as many problems or support as many use cases. Instead I focused on making the package smaller1 and faster. I also put a lot of thought into making the API (and documentation) as beginner-friendly as possible (with the caveat that windowing is still kind of an advanced use case).

If react-window provides the functionality your project needs, I would strongly recommend using it instead of react-virtualized. However if you need features that only react-virtualized provides, you have two options:

  1. Use react-virtualized. (It's still widely used by a lot of successful projects!)
  2. Create a component that decorates one of the react-window primitives and adds the functionality you need. You may even want to release this component to NPM (as its own, standalone package)! slightly_smiling_face

1 - Adding a react-virtualized list to a CRA project increases the (gzipped) build size by ~33.5 KB. Adding a react-window list to a CRA project increases the (gzipped) build size by <2 KB.

Can a list or a grid fill 100% the width or height of a page?

Yes. I recommend using the react-virtualized-auto-sizer package:

screen shot 2019-03-07 at 7 29 08 pm

Here's a Code Sandbox demo.

Why is my list blank when I scroll?

If your list looks something like this...

...then you probably forgot to use the style parameter! Libraries like react-window work by absolutely positioning the list items (via an inline style), so don't forget to attach it to the DOM element you render!

screen shot 2019-03-07 at 7 21 48 pm

Can I lazy load data for my list?

Yes. I recommend using the react-window-infinite-loader package:

screen shot 2019-03-07 at 7 32 32 pm

Here's a Code Sandbox demo.

Can I attach custom properties or event handlers?

Yes, using the outerElementType prop.

Screen Shot 2019-03-12 at 8 58 09 AM

Here's a Code Sandbox demo.

Can I add padding to the top and bottom of a list?

Yes, although it requires a bit of inline styling.

Screen Shot 2019-06-02 at 8 38 18 PM

Here's a Code Sandbox demo.

Can I add gutter or padding between items?

Yes, although it requires a bit of inline styling.

Screen Shot 2019-03-26 at 6 33 56 PM

Here's a Code Sandbox demo.

Does this library support "sticky" items?

Yes, although it requires a small amount of user code. Here's a Code Sandbox demo.

Reference

Read more at github or here.