
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@aiquants/virtualscroll
Advanced tools
High-performance virtual scrolling component for React with variable item heights
High-performance virtual scrolling component for React with variable item heights using Fenwick Tree optimization.
npm install @aiquants/virtualscroll
# or
yarn add @aiquants/virtualscroll
# or
pnpm add @aiquants/virtualscroll
import { VirtualScroll } from '@aiquants/virtualscroll'
import { useCallback } from 'react'
type Item = {
id: number
text: string
height: number
}
const items: Item[] = Array.from({ length: 10000 }, (_, i) => ({
id: i,
text: `Item ${i}`,
height: Math.floor(Math.random() * 50) + 30, // Random height between 30-80px
}))
function App() {
const getItem = useCallback((index: number) => items[index], [])
const getItemHeight = useCallback((index: number) => items[index].height, [])
return (
<div style={{ height: '400px', width: '100%' }}>
<VirtualScroll
itemCount={items.length}
getItem={getItem}
getItemHeight={getItemHeight}
viewportSize={400}
overscanCount={5}
className="border border-gray-300"
>
{(item, index) => (
<div
key={item.id}
style={{
height: item.height,
padding: '8px',
borderBottom: '1px solid #eee',
display: 'flex',
alignItems: 'center',
}}
>
<span>#{index}: {item.text}</span>
</div>
)}
</VirtualScroll>
</div>
)
}
| Prop | Type | Required | Description |
|---|---|---|---|
children | (item: T, index: number) => ReactNode | ✅ | Render function for items |
itemCount | number | ✅ | Total number of items |
getItem | (index: number) => T | ✅ | Function to get item at index |
getItemHeight | (index: number) => number | ✅ | Function to get item height |
viewportSize | number | ✅ | Height of the visible area |
overscanCount | number | ❌ | Number of items to render outside viewport (default: 5) |
className | string | ❌ | CSS class name |
onScroll | (position: number, totalHeight: number) => void | ❌ | Scroll event handler |
onRangeChange | (range: VirtualScrollRange) => void | ❌ | Range change handler |
background | ReactNode | ❌ | Background element |
initialScrollIndex | number | ❌ | Initial scroll index |
initialScrollOffset | number | ❌ | Initial scroll offset |
contentInsets | ScrollPaneContentInsets | ❌ | Insets for the content area |
callbackThrottleMs | number | ❌ | Throttle time for scroll callbacks (default: 5ms) |
onItemFocus | (index: number) => void | ❌ | Callback when an item is focused |
scrollBarOptions | VirtualScrollScrollBarOptions | ❌ | Options for the scrollbar |
behaviorOptions | VirtualScrollBehaviorOptions | ❌ | Options for scrolling behavior |
| Property | Type | Description |
|---|---|---|
width | number | Width of the scrollbar |
enableThumbDrag | boolean | Enable dragging the scrollbar thumb |
enableTrackClick | boolean | Enable clicking the scrollbar track |
enableArrowButtons | boolean | Enable arrow buttons on the scrollbar |
enableScrollToTopBottomButtons | boolean | Enable "Scroll to Top" and "Scroll to Bottom" buttons |
renderThumbOverlay | (props: ScrollBarThumbOverlayRenderProps) => ReactNode | Render prop to anchor custom UI near the scrollbar thumb |
tapScrollCircleOptions | ScrollBarTapCircleOptions | Customization for the auxiliary tap scroll circle |
| Property | Type | Description |
|---|---|---|
enablePointerDrag | boolean | Enable dragging the content area to scroll |
enableKeyboardNavigation | boolean | Enable keyboard navigation (default: true) |
wheelSpeedMultiplier | number | Multiplier for mouse wheel scrolling speed |
inertiaOptions | ScrollPaneInertiaOptions | Physics tuning for drag inertia |
clipItemHeight | boolean | Whether to clip item height (default: false) |
resetOnGetItemHeightChange | boolean | Whether to reset internal height cache when getItemHeight changes (default: false) |
| Method | Type | Description |
|---|---|---|
scrollTo | (position: number) => void | Scroll to specific position |
scrollToIndex | (index: number, options?: { align?: "top" | "bottom" | "center"; offset?: number }) => void | Scroll to specific item index with optional alignment and offset |
getScrollPosition | () => number | Get current scroll position |
getContentSize | () => number | Get total content size |
getViewportSize | () => number | Get viewport size |
focusItemAtIndex | (index: number, options?: { ensureVisible?: boolean }) => void | Focus item at specific index |
getRange | () => VirtualScrollRange | Get current range information |
| Property | Type | Description |
|---|---|---|
renderingStartIndex | number | Index of the first item being rendered (including overscan) |
renderingEndIndex | number | Index of the last item being rendered (including overscan) |
visibleStartIndex | number | Index of the first fully or partially visible item |
visibleEndIndex | number | Index of the last fully or partially visible item |
scrollPosition | number | Current scroll position in pixels |
totalHeight | number | Total height of the scroll content |
import { ScrollBarThumbOverlayRenderProps, VirtualScroll, VirtualScrollHandle } from '@aiquants/virtualscroll'
import { useCallback, useRef, useState } from 'react'
const items = Array.from({ length: 100000 }, (_, index) => ({
id: index,
text: `Item ${index}`,
height: (index % 20) * 2 + 30,
}))
const getItem = (index: number) => items[index]
const getItemHeight = (index: number) => items[index].height
function AdvancedExample() {
const virtualScrollRef = useRef<VirtualScrollHandle>(null)
const [visibleStartIndex, setVisibleStartIndex] = useState(0)
const scrollToTop = () => {
virtualScrollRef.current?.scrollTo(0)
}
const scrollToIndex = (index: number) => {
// Scroll to item 500, aligning it to the center of the viewport
virtualScrollRef.current?.scrollToIndex(index, { align: "center" })
}
const handleRangeChange = useCallback((
_renderingStartIndex: number,
_renderingEndIndex: number,
visibleStart: number,
_visibleEndIndex: number,
_scrollPosition: number,
_totalHeight: number,
) => {
setVisibleStartIndex(visibleStart)
}, [])
const renderThumbOverlay = useCallback((props: ScrollBarThumbOverlayRenderProps) => {
if (!(props.isDragging || props.isTapScrollActive)) {
return null
}
const activeItem = items[visibleStartIndex]
const label = activeItem ? activeItem.text : `Item ${visibleStartIndex}`
return (
<div
className="pointer-events-none absolute flex items-center"
style={
props.orientation === 'vertical'
? { top: props.thumbCenter, left: -14, transform: 'translate(-100%, -50%)' }
: { left: props.thumbCenter, top: -14, transform: 'translate(-50%, -100%)' }
}
>
<div className="rounded-full border border-slate-200 bg-white px-2 py-1 text-xs font-medium text-slate-700 shadow-md">
{label}
</div>
</div>
)
}, [visibleStartIndex])
return (
<div>
<div>
<button onClick={scrollToTop}>Scroll to Top</button>
<button onClick={() => scrollToIndex(500)}>Scroll to Item 500</button>
</div>
<VirtualScroll
ref={virtualScrollRef}
itemCount={items.length}
getItem={getItem}
getItemHeight={getItemHeight}
viewportSize={400}
onRangeChange={handleRangeChange}
scrollBarOptions={{
renderThumbOverlay: renderThumbOverlay
}}
>
{(item, index) => <ItemComponent item={item} index={index} />}
</VirtualScroll>
</div>
)
}
<VirtualScroll
itemCount={items.length}
getItem={getItem}
getItemHeight={getItemHeight}
viewportSize={400}
className="custom-virtual-scroll"
>
{(item, index) => <ItemComponent item={item} index={index} />}
</VirtualScroll>
<style>
.custom-virtual-scroll .scrollbar {
background-color: #f0f0f0;
}
.custom-virtual-scroll .scrollbar-thumb {
background-color: #007acc;
border-radius: 4px;
}
</style>
The auxiliary tap scroll circle can replace the native scrollbar for large datasets while remaining easy to control:
120× multiplier as itemCount grows (trillions supported).maxSpeedMultiplier when you need deterministic behavior.maxSpeedCurve, blending gentle near-threshold drag with high-speed travel at extended distances. Use easedOffset to slightly raise the entry-level speed without changing the ceiling.size, offsetX, offsetY) keeps the circle accessible on both desktop and touch devices.opacity knob so you can match subdued or high-contrast UI themes.import { VirtualScroll } from "@aiquants/virtualscroll"
const items = Array.from({ length: 1_000_000_000_000 }, (_, index) => ({
id: index,
text: `Record ${index}`,
height: 42,
}))
export function UltraFastExample() {
return (
<VirtualScroll
itemCount={items.length}
getItem={(index) => items[index]}
getItemHeight={() => 42}
viewportSize={480}
scrollBarOptions={{
tapScrollCircleOptions: {
maxSpeedMultiplier: 80, // Optional: override adaptive speed when needed
maxSpeedCurve: {
exponentialSteepness: 6,
exponentialScale: 80,
easedOffset: 0.1,
},
offsetX: -96,
opacity: 0.85,
}
}}
>
{(item) => <div style={{ height: item.height }}>{item.text}</div>}
</VirtualScroll>
)
}
useCallback for getItem and getItemHeightMIT
FAQs
High-performance virtual scrolling component for React with variable item heights
We found that @aiquants/virtualscroll demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.