Gathering detailed insights and metrics for react-viewport-list
Gathering detailed insights and metrics for react-viewport-list
Gathering detailed insights and metrics for react-viewport-list
Gathering detailed insights and metrics for react-viewport-list
viewport-render-list
Virtual scroll list for react
react-large-virtualized-list
React component for efficiently rendering large lists (Only renders items visible in the current viewport)
simple-virtualization
Virtualization for React
react-virtual-dynamics
A React component for efficiently rendering large lists or grids of items using virtualization. This component improves performance by only rendering items that are currently visible in the viewport. Additionally, it supports infinite scrolling by dynamic
📜 Virtualization for lists with dynamic item size
npm install react-viewport-list
Typescript
Module System
Node Version
NPM Version
96.3
Supply Chain
100
Quality
76.1
Maintenance
100
Vulnerability
100
License
TypeScript (99.82%)
Shell (0.18%)
Total Downloads
2,539,107
Last Day
4,455
Last Week
30,987
Last Month
132,438
Last Year
1,349,350
MIT License
232 Stars
134 Commits
20 Forks
7 Watchers
3 Branches
3 Contributors
Updated on Jan 24, 2025
Minified
Minified + Gzipped
Latest Version
7.1.2
Package Id
react-viewport-list@7.1.2
Unpacked Size
52.60 kB
Size
10.68 kB
File Count
5
NPM Version
10.2.3
Node Version
20.10.0
Published on
Dec 07, 2023
Cumulative downloads
Total Downloads
Last Day
11.5%
4,455
Compared to previous day
Last Week
19.7%
30,987
Compared to previous week
Last Month
-1.6%
132,438
Compared to previous month
Last Year
44.8%
1,349,350
Compared to previous year
1
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.
height
or width
(if you don't know item size)position: absolute
)Try 100k list demo
1npm install --save react-viewport-list
1import { useRef } from 'react'; 2import { ViewportList } from 'react-viewport-list'; 3 4const ItemList = ({ 5 items, 6}: { 7 items: { id: string; title: string }[]; 8}) => { 9 const ref = useRef<HTMLDivElement | null>( 10 null, 11 ); 12 13 return ( 14 <div className="scroll-container" ref={ref}> 15 <ViewportList 16 viewportRef={ref} 17 items={items} 18 > 19 {(item) => ( 20 <div key={item.id} className="item"> 21 {item.title} 22 </div> 23 )} 24 </ViewportList> 25 </div> 26 ); 27}; 28 29export { ItemList };
MutableRefObject<HTMLElement / null> / RefObject<HTMLElement / null> / { current: HTMLElement / null } / null
name | type | default | description |
---|---|---|---|
viewportRef | MutableRefObject<HTMLElement / null> / RefObject<HTMLElement / null> / { current: HTMLElement / null } / null | required | Viewport and scroll container.document.documentElement will be used if viewportRef not provided. |
items | T[] | [] | Array of items. |
itemSize | number | 0 | Item average (estimated) size (height for axis="y" and width for axis="x" ) in px.Size should be greater or equal zero. Size will be computed automatically if itemMinSize not provided or equal zero. |
itemMargin | number | -1 | Item margin (margin-bottom for axis="y" and margin-right for axis="x" ) in px.Margin should be greater or equal -1. Margin will be computed automatically if margin not provided or equal -1.You should still set margin in item styles |
overscan | number | 1 | Count of "overscan" items. |
axis | "y" / "x" | 'y' | Scroll axis:
|
initialIndex | number | -1 | Initial item index in viewport. |
initialAlignToTop | boolean | true | scrollIntoView param. Used with initialIndex |
initialOffset | number | 0 | Offset after scrollIntoView call.Used with initialIndex .This value will be added to the scroll after scroll to index. |
initialDelay | number | -1 | setTimeout delay for initial scrollToIndex .Used with initialIndex . |
initialPrerender | number | 0 | Used with initialIndex .This value will modify initial start index and initial end index like [initialIndex - initialPrerender, initialIndex + initialPrerender] .You can use it to avoid blank screen with only one initial item rendered |
children | (item: T, index: number, array: T[]) => ReactNode | required | Item render function. Similar to Array.Prototype.map() . |
onViewportIndexesChange | (viewportIndexes: [number, number]) => void | optional | Will be called on rendered in viewport indexes change. |
overflowAnchor | "none" / "auto" | "auto" | Compatibility for overflow-anchor: none .Set it to "none" if you use overflow-anchor: none in your parent container styles. |
withCache | boolean | true | Cache rendered item heights. |
scrollThreshold | number | 0 | If scroll diff more than scrollThreshold setting indexes was skipped. It's can be useful for better fast scroll UX. |
renderSpacer | (props: { ref: MutableRefObject | ({ ref, style }) => <div ref={ref} style={style} /> | In some rare cases you can use specific elements/styles instead of default spacers |
count | number | optional | You can use items count instead of items directly. Use should use different children: (index: number) => ReactNode |
indexesShift | number | 0 | Every time you unshift (prepend items) you should increase indexesShift by prepended items count. If you shift items (remove items from top of the list you should decrease indexesShift by removed items count). |
getItemBoundingClientRect | (element: Element) => DOMRect / { bottom: number; left: number; right: number; top: number; width: number; height: number; } | (element) => element.getBoundingClientRect() | You can use custom rect getter to support display: contents or other cases when element.getBoundingClientRect() returns "bad" data |
scrollToIndex method has only one param - options;
Options param
name | type | default | description |
---|---|---|---|
index | number | -1 | Item index for scroll. |
alignToTop | boolean | true | scrollIntoView param. Only boolean option supported. |
offset | number | 0 | Offset after scrollIntoView call.This value will be added to the scroll after scroll to index. |
delay | number | -1 | setTimeout delay for initial scrollToIndex . |
prerender | number | 0 | This value will modify initial start index and initial end index like [index - initialPrerender, index + initialPrerender] .You can use it to avoid blank screen with only one initial item rendered |
Usage
1import { useRef } from 'react'; 2import { ViewportList } from 'react-viewport-list'; 3 4const ItemList = ({ 5 items, 6}: { 7 items: { id: string; title: string }[]; 8}) => { 9 const ref = useRef(null); 10 const listRef = useRef(null); 11 12 return ( 13 <div className="scroll-container" ref={ref}> 14 <ViewportList 15 ref={listRef} 16 viewportRef={ref} 17 items={items} 18 > 19 {(item) => ( 20 <div key={item.id} className="item"> 21 {item.title} 22 </div> 23 )} 24 </ViewportList> 25 <button 26 className="up-button" 27 onClick={() => 28 listRef.current.scrollToIndex({ 29 index: 0, 30 }) 31 } 32 /> 33 </div> 34 ); 35}; 36 37export { ItemList };
getScrollPosition returns an object with scroll position: { index: number, offset: number }
Returns
name | type | description |
---|---|---|
index | number | Item index for scroll. |
offset | number | Offset after scrollIntoView call.This value will be added to the scroll after scroll to index. |
If items=[]
or count=0
getScrollPosition returns { index: -1; offset: 0 }
Usage
1import { useEffect, useRef } from 'react'; 2import { ViewportList } from 'react-viewport-list'; 3 4const ItemList = ({ 5 items, 6}: { 7 items: { id: string; title: string }[]; 8}) => { 9 const ref = useRef(null); 10 const listRef = useRef(null); 11 12 useEffect( 13 () => () => { 14 window.sessionStorage.setItem( 15 'lastScrollPosition', 16 JSON.stringify( 17 listRef.current.getScrollPosition(), 18 ), 19 ); 20 }, 21 [], 22 ); 23 24 return ( 25 <div className="scroll-container" ref={ref}> 26 <ViewportList 27 ref={listRef} 28 viewportRef={ref} 29 items={items} 30 > 31 {(item) => ( 32 <div key={item.id} className="item"> 33 {item.title} 34 </div> 35 )} 36 </ViewportList> 37 <button className="up-button" /> 38 </div> 39 ); 40}; 41 42export { ItemList };
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 instead of fixing them.
1.scroll-container { 2 will-change: transform; 3}
ViewportList
render two elements (spacers) before first rendered item and after last rendered item.
That's why children pseudo-classes like :nth-child()
, :last-child
, :first-child
may work incorrectly.
If you want more accurate virtualizing you should use equal margin for all items.
Also, you should use margin-top
or margin-bottom
(not both) for axis="y"
and margin-right
or margin-left
(not both) for axis="x"
.
If you want to use different margins and stil want more accurate virtualizing you can wrap your items in some element like <div>
and use padding
instead of margin
.
You should avoid non-keyed usage of list. You should provide unique key prop for each list items.
If you have issues with scroll in Safari and other browsers without overflow-anchor
support, check item's key
prop.
ViewportList
render Fragment
with items in viewport. So, grouping just work.
1import { useRef } from 'react'; 2import { ViewportList } from 'react-viewport-list'; 3 4const GroupedItemList = ({ 5 keyItems, 6 items, 7}: { 8 keyItems: { id: string; title: string }[]; 9 items: { id: string; title: string }[]; 10}) => { 11 const ref = useRef(null); 12 13 return ( 14 <div className="scroll-container" ref={ref}> 15 <span className="group-title"> 16 Key Items 17 </span> 18 <ViewportList 19 viewportRef={ref} 20 items={keyItems} 21 > 22 {(item) => ( 23 <div 24 key={item.id} 25 className="key-item" 26 > 27 {item.title} 28 </div> 29 )} 30 </ViewportList> 31 <span className="group-title">Items</span> 32 <ViewportList 33 viewportRef={ref} 34 items={items} 35 > 36 {(item) => ( 37 <div key={item.id} className="item"> 38 {item.title} 39 </div> 40 )} 41 </ViewportList> 42 </div> 43 ); 44}; 45export { GroupedItemList };
You can use React Sortable HOC
1import { useRef } from 'react'; 2import { 3 SortableContainer, 4 SortableElement, 5} from 'react-sortable-hoc'; 6import { ViewportList } from 'react-viewport-list'; 7 8const SortableList = SortableContainer( 9 ({ innerRef, ...rest }) => ( 10 <div {...rest} ref={innerRef} /> 11 ), 12); 13 14const SortableItem = SortableElement( 15 (props) => <div {...props} />, 16); 17 18const SortableItemList = ({ 19 items, 20 onSortEnd, 21}) => { 22 const ref = useRef(null); 23 24 return ( 25 <SortableList 26 innerRef={ref} 27 className="scroll-container" 28 onSortEnd={onSortEnd} 29 > 30 <ViewportList 31 viewportRef={ref} 32 items={items} 33 > 34 {(item, index) => ( 35 <SortableItem 36 key={index} 37 index={index} 38 className="item" 39 > 40 {item.title} 41 </SortableItem> 42 )} 43 </ViewportList> 44 </SortableList> 45 ); 46}; 47 48export { SortableItemList };
Scroll to position may work incorrectly because scrollHeight and scrollTop (or scrollWidth and scrollLeft) changed automatically while scrolling.
But you can scroll to position with scrollToIndex
method with { index: 0, offset: scrollPosition }
. For initial scroll to position you can use initialIndex={0}
and initialOffset={scrollPosition}
. You should remember that after scroll happened scroll position can be not equal to specified offset.
1import { useRef } from 'react'; 2import { ViewportList } from 'react-viewport-list'; 3 4const ItemList = ({ 5 items, 6 savedScroll, 7}: { 8 items: { id: string; title: string }[]; 9 savedScroll: number; 10}) => { 11 const ref = useRef(null); 12 const listRef = useRef(null); 13 14 return ( 15 <div className="scroll-container" ref={ref}> 16 <ViewportList 17 ref={listRef} 18 viewportRef={ref} 19 items={items} 20 initialIndex={0} 21 initialOffset={savedScroll} 22 > 23 {(item) => ( 24 <div key={item.id} className="item"> 25 {item.title} 26 </div> 27 )} 28 </ViewportList> 29 <button 30 className="up-button" 31 onClick={() => { 32 // this sets scrollTop of "scroll-container" to 1000 33 listRef.current.scrollToIndex({ 34 index: 0, 35 offset: 1000, 36 }); 37 }} 38 /> 39 </div> 40 ); 41}; 42 43export { ItemList };
You can mock ViewportList for unit tests:
1import { 2 useImperativeHandle, 3 forwardRef, 4} from 'react'; 5 6export const ViewportListMock = forwardRef( 7 ({ items = [], children }, ref) => { 8 useImperativeHandle( 9 ref, 10 () => ({ 11 scrollToIndex: () => {}, 12 }), 13 [], 14 ); 15 16 return ( 17 <> 18 <div /> 19 {items.map(children)} 20 <div /> 21 </> 22 ); 23 }, 24); 25 26export default ViewportListMock;
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
4 existing vulnerabilities detected
Details
Reason
Found 0/8 approved changesets -- score normalized to 0
Reason
0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-05-05
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More