npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@virt-list/react

v0.0.9

Published

React virtual list, grid and tree components

Readme

@virt-list/react

npm

React 18+ 的虚拟列表 / 网格 / 树形组件。不定高无需声明高度,三十万行与三十行的 DOM 节点数相同。

算法在框架无关的内核里,这个包只是一层薄绑定(每个组件约 300 行)。 React 16-17 用 @virt-list/react-legacy, 两者组件 API 完全一致。

安装

pnpm add @virt-list/react

peer 依赖:react >= 18react-dom >= 18(用到 createRootflushSync)。

引入样式

滚动条是自绘的,样式必须引一次——不引也能滚,但看不到滚动条。用到 VirtTree 时 这一份也提供树的默认外观。放在应用入口即可:

// main.tsx
import '@virt-list/react/style.css';

组件的 JS 里刻意不 import 这份 css:那会让整个包在纯 Node 环境(SSR)下 import 不了 ——Node 不认 .css 扩展名。可定制项都以 CSS 变量暴露(--virt-scrollbar-* / --virt-tree-*), 覆盖变量即可换肤;暗色模式跟随宿主的 html.dark 或任意祖先上的 [data-theme='dark']

最小示例

import { useRef, useState } from 'react';
import { VirtList, type VirtListRef } from '@virt-list/react';

type Item = { id: number; text: string };

export default function App() {
  const [list] = useState<Item[]>(() =>
    Array.from({ length: 100000 }, (_, i) => ({ id: i, text: `item-${i}` })),
  );
  const listRef = useRef<VirtListRef<Item>>(null);

  return (
    // 容器必须有确定的高度,组件不会自己撑开
    <div style={{ width: 500, height: 400 }}>
      <VirtList
        ref={listRef}
        list={list}
        itemKey='id'
        estimatedSize={40}
        // e 是普通对象,不是 React 合成事件:offset / delta / direction / atStart / atEnd / source
        onScroll={(e) => e.atEnd && console.log('到底了')}
      >
        {({ itemData, index }) => (
          <div>
            {index} — {itemData.text}
          </div>
        )}
      </VirtList>
    </div>
  );
}

// listRef.current?.scrollToIndex(50000, { behavior: 'smooth' });

list 建议保持引用稳定(useState / useMemo),每次渲染都新建一个数组会让组件反复重算。

导出

| 导出 | 说明 | | --- | --- | | VirtList | 虚拟列表,支持不定高、水平滚动、sticky 区域、空状态、无限加载 | | VirtGrid | 虚拟网格,gridItems 指定每行列数,按行虚拟化 | | VirtTree | 虚拟树,展开 / 选择 / 勾选 / 过滤 / 拖拽排序 | | useVirtList | 无头 Hook,DOM 结构完全自己写时用 | | createStreamBuffer | 流式输出的帧内合并(从 core 转出,免得你为它多依赖一个包) | | 类型 | VirtListProps / VirtListRef / VirtGridProps / VirtTreeProps / TreeNode / VirtScrollEvent / LoadState 等 |

常用 props / 事件 / 方法

必填     list、itemKey、estimatedSize
尺寸     itemGap、fixedSize、horizontal
渲染     buffer / bufferTop / bufferBottom、renderControl、itemClass / itemStyle、listClass / listStyle
定位     initialIndex、initialOffset、initialPosition、scrollDuration、smoothMaxDistance
加载     loadMore、hasMoreTop、hasMoreBottom、edgeThreshold
贴底     stickyBottom、stickyThreshold
复制     copyText、copySeparator
交互     keyboard、scrollbarAutoHideDelay、scrollbarMinThumbSize

渲染函数:children({ itemData, index })renderHeader(loadState) / renderFooter(loadState) (加载提示条直接据此渲染)、renderStickyHeader()renderStickyFooter()renderEmpty()

事件:onScrollonOffsetChangeonToToponToBottomonItemResizeonUpdateonLoadStateChange

ref 方法:

scrollToIndex(index, opts?) / scrollIntoView(index, opts?)
scrollToTop(opts?) / scrollToBottom(opts?) / scrollToOffset(offset, opts?) / cancelScroll()
// opts: { behavior: 'auto' | 'smooth', align: 'start' | 'end', focus, duration, onDone }

getState() / getOffset() / getMaxOffset() / getTotalSize() / getItemSize(key)
getItemPosByIndex(index) / getIndexByOffset(offset) / getLoadState()
setList(list) / forceUpdate() / reset() / resume()

完整参数表见文档站的 API 一节。

用之前需要知道的

  • estimatedSize 必填:首屏布局与未测量项的占位依据,不定高时越接近实测值抖动越少。
  • 容器要有确定的高度(水平模式下是宽度),但不需要给项声明高度——行高由内容决定。
  • 偏移量归 JS 掌管:容器是 overflow: hiddenscrollTop 恒为 0。读写滚动位置用 getOffset() / scrollToOffset()onScroll 的载荷是 VirtScrollEvent 而不是 React 合成事件(多了 source 字段,能确定地区分用户滚动 / 程序定位 / 内部补偿)。组件只把 style / className 透传给根节点,事件 props 一律不透传,因此不会与合成事件打架。
  • loadMore 里自己往 list 写数据setState),返回该方向是否还有更多。防重入、位移补偿、 不足一屏时自动续拉、loading 状态透出都由库负责。
  • 需要原生滚动条的项目请用旧库 vue-virt-list

文档

许可

MIT