New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@aiquants/virtualscroll

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aiquants/virtualscroll

High-performance virtual scrolling component for React with variable item heights

latest
npmnpm
Version
1.15.0
Version published
Maintainers
2
Created
Source

@aiquants/virtualscroll

High-performance virtual scrolling component for React with variable item heights using Fenwick Tree optimization.

Features

  • High Performance: Optimized for thousands of items with O(log n) operations
  • 📐 Variable Heights: Support for items with different heights
  • 🎯 Precise Scrolling: Accurate scroll positioning and smooth navigation
  • 📱 Touch Support: Full support for touch devices
  • 🎨 Customizable: Flexible styling and theming options
  • 🌀 Ultrafast Tap Scroll: Adaptive tap scroll circle that scales speed up to 120× for massive datasets
  • 🔧 TypeScript: Full TypeScript support with comprehensive type definitions

Installation

npm install @aiquants/virtualscroll
# or
yarn add @aiquants/virtualscroll
# or
pnpm add @aiquants/virtualscroll

Basic Usage

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>
  )
}

API Reference

VirtualScroll Props

PropTypeRequiredDescription
children(item: T, index: number) => ReactNodeRender function for items
itemCountnumberTotal number of items
getItem(index: number) => TFunction to get item at index
getItemHeight(index: number) => numberFunction to get item height
viewportSizenumberHeight of the visible area
overscanCountnumberNumber of items to render outside viewport (default: 5)
classNamestringCSS class name
onScroll(position: number, totalHeight: number) => voidScroll event handler
onRangeChange(range: VirtualScrollRange) => voidRange change handler
backgroundReactNodeBackground element
initialScrollIndexnumberInitial scroll index
initialScrollOffsetnumberInitial scroll offset
contentInsetsScrollPaneContentInsetsInsets for the content area
callbackThrottleMsnumberThrottle time for scroll callbacks (default: 5ms)
onItemFocus(index: number) => voidCallback when an item is focused
scrollBarOptionsVirtualScrollScrollBarOptionsOptions for the scrollbar
behaviorOptionsVirtualScrollBehaviorOptionsOptions for scrolling behavior

VirtualScrollScrollBarOptions

PropertyTypeDescription
widthnumberWidth of the scrollbar
enableThumbDragbooleanEnable dragging the scrollbar thumb
enableTrackClickbooleanEnable clicking the scrollbar track
enableArrowButtonsbooleanEnable arrow buttons on the scrollbar
enableScrollToTopBottomButtonsbooleanEnable "Scroll to Top" and "Scroll to Bottom" buttons
renderThumbOverlay(props: ScrollBarThumbOverlayRenderProps) => ReactNodeRender prop to anchor custom UI near the scrollbar thumb
tapScrollCircleOptionsScrollBarTapCircleOptionsCustomization for the auxiliary tap scroll circle

VirtualScrollBehaviorOptions

PropertyTypeDescription
enablePointerDragbooleanEnable dragging the content area to scroll
enableKeyboardNavigationbooleanEnable keyboard navigation (default: true)
wheelSpeedMultipliernumberMultiplier for mouse wheel scrolling speed
inertiaOptionsScrollPaneInertiaOptionsPhysics tuning for drag inertia
clipItemHeightbooleanWhether to clip item height (default: false)
resetOnGetItemHeightChangebooleanWhether to reset internal height cache when getItemHeight changes (default: false)

VirtualScrollHandle Methods

MethodTypeDescription
scrollTo(position: number) => voidScroll to specific position
scrollToIndex(index: number, options?: { align?: "top" | "bottom" | "center"; offset?: number }) => voidScroll to specific item index with optional alignment and offset
getScrollPosition() => numberGet current scroll position
getContentSize() => numberGet total content size
getViewportSize() => numberGet viewport size
focusItemAtIndex(index: number, options?: { ensureVisible?: boolean }) => voidFocus item at specific index
getRange() => VirtualScrollRangeGet current range information

VirtualScrollRange

PropertyTypeDescription
renderingStartIndexnumberIndex of the first item being rendered (including overscan)
renderingEndIndexnumberIndex of the last item being rendered (including overscan)
visibleStartIndexnumberIndex of the first fully or partially visible item
visibleEndIndexnumberIndex of the last fully or partially visible item
scrollPositionnumberCurrent scroll position in pixels
totalHeightnumberTotal height of the scroll content

Advanced Usage

With Ref and Scroll Control

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>
  )
}

Custom Scrollbar Styling

<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>

Tap Scroll Circle Configuration

The auxiliary tap scroll circle can replace the native scrollbar for large datasets while remaining easy to control:

  • Adaptive speed scaling automatically ramps up to a 120× multiplier as itemCount grows (trillions supported).
  • Manual overrides let you clamp or extend speed via maxSpeedMultiplier when you need deterministic behavior.
  • Exponential capping enables distance-sensitive ceilings with 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.
  • Full layout control (size, offsetX, offsetY) keeps the circle accessible on both desktop and touch devices.
  • Visibility tuning exposes an 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>
  )
}

Performance Tips

  • Memoize callback functions: Use useCallback for getItem and getItemHeight
  • Optimize item rendering: Memoize item components when possible
  • Adjust overscan count: Balance between smooth scrolling and memory usage
  • Consider item height consistency: More consistent heights provide better performance

Browser Support

  • Chrome 88+
  • Firefox 87+
  • Safari 14+
  • Edge 88+

License

MIT

Keywords

react

FAQs

Package last updated on 11 Feb 2026

Did you know?

Socket

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.

Install

Related posts