Installations
npm install react-viewport-list
Developer Guide
Typescript
Yes
Module System
CommonJS
Node Version
20.10.0
NPM Version
10.2.3
Score
96.1
Supply Chain
100
Quality
76.1
Maintenance
100
Vulnerability
100
License
Releases
Contributors
Unable to fetch Contributors
Languages
TypeScript (99.82%)
Shell (0.18%)
Love this project? Help keep it running — sponsor us today! 🚀
Developer
Download Statistics
Total Downloads
2,129,476
Last Day
5,719
Last Week
28,927
Last Month
116,934
Last Year
1,235,191
GitHub Statistics
232 Stars
134 Commits
19 Forks
8 Watching
3 Branches
3 Contributors
Bundle Size
7.93 kB
Minified
2.93 kB
Minified + Gzipped
Package Meta Information
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
Publised On
07 Dec 2023
Total Downloads
Cumulative downloads
Total Downloads
2,129,476
Last day
11%
5,719
Compared to previous day
Last week
20.9%
28,927
Compared to previous week
Last month
42.2%
116,934
Compared to previous month
Last year
61.3%
1,235,191
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Peer Dependencies
1
React ViewPort List
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.
📜 Virtualization for lists with dynamic item size
Features 🔥
- Simple API like Array.Prototype.map()
- Created for dynamic item
height
orwidth
(if you don't know item size) - Works perfectly with Flexbox (unlike other libraries with
position: absolute
) - Supports scroll to index
- Supports initial index
- Supports vertical ↕ and horizontal ↔ lists️️
- Tiny (about 2kb minified+gzipped)
Try 100k list demo
Getting Started
-
Installation:
1npm install --save react-viewport-list
-
Basic 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<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
Props
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 |
Methods
scrollToIndex
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
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 };
Performance
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}
Children pseudo-classes
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.
Margin
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
.
Non-keyed
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.
Advanced Usage
-
Grouping
ViewportList
renderFragment
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 };
-
Sorting
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
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 useinitialIndex={0}
andinitialOffset={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 };
-
Tests
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;
![Empty State](/_next/static/media/empty.e5fae2e5.png)
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
3 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
Reason
0 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 1
Reason
Found 0/8 approved changesets -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 23 are checked with a SAST tool
Score
2.8
/10
Last Scanned on 2025-02-03
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 MoreOther packages similar to 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)
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
card-window
Card window works by only rendering part of a large data set (just enough to fill the viewport). It is very much inspired by Brian Vaughn's react-window.