If your application renders long lists of data (hundreds or thousands of rows), we recommended using a technique known as βwindowingβ. This technique only renders a small subset of your rows at any given time, and can dramatically reduce the time it takes to re-render the components as well as the number of DOM nodes created.
React Component that render only items in viewport
- Simple API like .map()
- Created for dynamic item
height
/width
(if you don't know item size) - Works perfectly with Flexbox (unlike other libraries with
pisition: absolute
) - Supports scroll to index
- Supports initial index
- Supports vertical β and horizontal β listsοΈοΈ
- Tiny (<2kb minified+gzipped)
Try 100k list demo
-
npm install --save react-viewport-list
-
import { useRef } from 'react'; import ViewportList from 'react-viewport-list'; const ItemsList = ({ items }) => { const ref = useRef(null); return ( <div className="scroll-container" ref={ref}> <ViewportList viewportRef={ref} items={items} itemMinSize={40} margin={8}> {(item) => ( <div key={item.id} className="item"> {item.title} </div> )} </ViewportList> </div> ); }; export default ItemsList;
name | type | default | description |
---|---|---|---|
viewportRef | MutableRefObject | required | Viewport ref object |
items | Array | [] | Array of items |
itemMinSize | number | required | Item min height (or min width for 'x' axis) in px |
margin | number | 0 | Item margin bottom (or margin right for 'x' axis) in px. You should still set margin-bottom (or margin-right for 'x' axis) in item styles |
overscan | number | 1 | Count of "overscan" items |
axis | 'y' / 'x' | 'y' | Scroll axis 'y' - vertical, 'x' - horizontal |
initialIndex | number | -1 | Initial index of item in viewport |
initialAlignToTop | boolean / ScrollIntoViewOptions | true | scrollIntoView second argument. Used with initialIndex |
initialOffset | number | 0 | Offset after scrollIntoView. Used with initialIndex |
children | (item: any, index: number) => ReactNode | required | Item render function. Similar to .map() callback |
-
Params
name type default description index number -1 Item index for scroll alignToTop boolean / ScrollIntoViewOptions true scrollIntoView second argument offset number 0 Offset after scrollIntoView Usage
import { useRef } from 'react'; import ViewportList from 'react-viewport-list'; const ItemsList = ({ items }) => { const ref = useRef(null); const listRef = useRef(null); return ( <div className="scroll-container" ref={ref}> <ViewportList ref={listRef} viewportRef={ref} items={items} itemMinSize={40} margin={8}> {(item) => ( <div key={item.id} className="item"> {item.title} </div> )} </ViewportList> <button className="up-button" onClick={() => listRef.current.scrollToIndex(0)} /> </div> ); }; export default ItemsList;
If you have performance issues, you can add will-change: transform
to a scroll container.
You should remember that in some situations will-change: transform
can cause performance issues not fixed them.
.scroll-container {
will-change: transform;
}
-
You should use only
margin-bottom
(ormargin-right
for 'x' axis) for items, and provide it to ViewportList props. Don't usemargin-top
(ormargin-left
for 'x' axis).item { margin-bottom: 8px; }
-
ViewportList render
Fragment
with items in viewportimport { useRef } from 'react'; import ViewportList from 'react-viewport-list'; const ItemsList = ({ keyItems, items }) => { const ref = useRef(null); return ( <div className="scroll-container" ref={ref}> <span className="group-title">{'Key Items'}</span> <ViewportList viewportRef={ref} items={keyItems} itemMinSize={60} margin={8}> {(item) => ( <div key={item.id} className="key-item"> {item.title} </div> )} </ViewportList> <span className="group-title">{'Items'}</span> <ViewportList viewportRef={ref} items={items} itemMinSize={40} margin={8}> {(item) => ( <div key={item.id} className="item"> {item.title} </div> )} </ViewportList> </div> ); }; export default ItemsList;
-
You can use React Sortable HOC
import { useRef } from 'react'; import { SortableContainer, SortableElement } from 'react-sortable-hoc'; import ViewportList from 'react-viewport-list'; const SortableList = SortableContainer(({ innerRef, ...rest }) => <div {...rest} ref={innerRef} />); const SortableItem = SortableElement((props) => <div {...props} />); const ItemsList = ({ items, onSortEnd }) => { const ref = useRef(null); return ( <SortableList innerRef={ref} className="scroll-container" onSortEnd={onSortEnd}> <ViewportList viewportRef={ref} items={items} itemMinSize={40} margin={8}> {(item, index) => ( <SortableItem key={index} index={index} className="item"> {item.title} </SortableItem> )} </ViewportList> </SortableList> ); }; export default ItemsList;