Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A zero-config, fast and small (~3kB) virtual list (and grid) component for React, Vue, Solid and Svelte.

License

NotificationsYou must be signed in to change notification settings

inokawa/virtua

Repository files navigation

npmnpm bundle sizenpmBest of JScheckdemo

A zero-config, fast and small (~3kB) virtual list (and grid) component forReact,Vue,Solid andSvelte.

example

If you want to check the difference with the alternatives right away,see comparison section.

Motivation

This project is a challenge to rethink virtualization. The goals are...

  • Zero-config virtualization: This library is designed to give the best performance without configuration. It also handles common hard things in the real world (dynamic size measurement, scroll position adjustment while reverse scrolling and imperative scrolling, iOS support, etc).
  • Fast: Natural virtual scrolling needs optimization in many aspects (eliminate frame drops by reducing CPU usage and GC, reducesynchronous layout recalculation, reduce visual jumps on repaint, optimize with CSS, optimize for frameworks, etc). We are trying to combine the best of them.
  • Small: Its bundle size should be small as much as possible to be friendly with modern web development. Currently each components are ~3kB gzipped and tree-shakeable. The total size for React is~5kB gzipped.
  • Flexible: Aiming to support many usecases - fixed size, dynamic size, horizontal scrolling, reverse scrolling, RTL, mobile, infinite scrolling, scroll restoration, DnD, keyboard navigation, sticky, placeholder and more. Seelive demo.
  • Framework agnostic:React,Vue,Solid andSvelte are supported. We could support other frameworks in the future.

Demo

https://inokawa.github.io/virtua/

Install

npm install virtua

If you use this lib inlegacy browsers which does not have ResizeObserver, you should usepolyfill.

Getting started

React

react >= 16.14 is required.

If you use ESM and webpack 5, use react >= 18 to avoidCan't resolvereact/jsx-runtime error.

Vertical scroll

import{VList}from"virtua";exportconstApp=()=>{return(<VListstyle={{height:800}}>{Array.from({length:1000}).map((_,i)=>(<divkey={i}style={{height:Math.floor(Math.random()*10)*10+10,borderBottom:"solid 1px gray",background:"white",}}>{i}</div>))}</VList>);};

Horizontal scroll

import{VList}from"virtua";exportconstApp=()=>{return(<VListstyle={{height:400}}horizontal>{Array.from({length:1000}).map((_,i)=>(<divkey={i}style={{width:Math.floor(Math.random()*10)*10+10,borderRight:"solid 1px gray",background:"white",}}>{i}</div>))}</VList>);};

Customization

VList is a recommended solution which works like a drop-in replacement of simple list built with scrollablediv (or removedvirtual-scroller element). For more complicated styling or markup, useVirtualizer.

import{Virtualizer}from"virtua";exportconstApp=()=>{return(<divstyle={{overflowY:"auto",height:800}}><divstyle={{height:40}}>header</div><VirtualizerstartMargin={40}>{Array.from({length:1000}).map((_,i)=>(<divkey={i}style={{height:Math.floor(Math.random()*10)*10+10,borderBottom:"solid 1px gray",background:"white",}}>{i}</div>))}</Virtualizer></div>);};

Window scroll

import{WindowVirtualizer}from"virtua";exportconstApp=()=>{return(<divstyle={{padding:200}}><WindowVirtualizer>{Array.from({length:1000}).map((_,i)=>(<divkey={i}style={{height:Math.floor(Math.random()*10)*10+10,borderBottom:"solid 1px gray",background:"white",}}>{i}</div>))}</WindowVirtualizer></div>);};

Vertical and horizontal scroll

import{experimental_VGridasVGrid}from"virtua";exportconstApp=()=>{return(<VGridstyle={{height:800}}row={1000}col={500}>{({ rowIndex, colIndex})=>(<divstyle={{width:((colIndex%3)+1)*100,border:"solid 1px gray",background:"white",}}>{rowIndex} /{colIndex}</div>)}</VGrid>);};

React Server Components (RSC) support

This library is marked as a Client Component. You can render RSC as children ofVList,Virtualizer orWindowVirtualizer.

// page.tsx in App Router of Next.jsexportdefaultasync()=>{constarticles=awaitfetchArticles();return(<div><div>This is Server Component</div><VListstyle={{height:300}}>{articles.map((a)=>(<divkey={a.id}style={{border:"solid 1px gray",height:80}}>{a.content}</div>))}</VList></div>);};

Vue

vue >= 3.2 is required.

<script setup>import {VList }from"virtua/vue";constsizes= [20,40,180,77];constdata=Array.from({ length:1000 }).map((_,i)=> sizes[i%4]);</script><template>  <VList:data="data":style="{ height: '800px' }"#default="{ item, index }">    <div:key="index":style="{        height: item + 'px',        background: 'white',        borderBottom: 'solid 1px #ccc',      }"    >      {{ index }}    </div>  </VList></template>

Solid

solid-js >= 1.0 is required.

import{VList}from"virtua/solid";exportconstApp=()=>{constsizes=[20,40,80,77];constdata=Array.from({length:1000}).map((_,i)=>sizes[i%4]);return(<VListdata={data}style={{height:"800px"}}>{(d,i)=>(<divstyle={{height:d+"px","border-bottom":"solid 1px #ccc",background:"#fff",}}>{i}</div>)}</VList>);};

Svelte

svelte >= 5.0 is required.

<scriptlang="ts">import {VList }from"virtua/svelte";const sizes= [20,40,180,77];const data=Array.from({ length:1000 }).map((_,i)=>sizes[i%4] );</script><VList {data}style="height: 100vh;"getKey={(_,i)=>i}>  {#snippetchildren(item,index)}    <divstyle="        height: {item}px;        background: white;        border-bottom: solid 1px #ccc;"    >      {index}    </div>  {/snippet}</VList>

Other bindings

Documentation

FAQs

Is there any way to improve performance further?

In complex usage, especially if you re-render frequently the parent of virtual scroller or the children are tons of items, children element creation can be a performance bottle neck. That's because creating React elements is fast enough but not free and new React element instances break some of memoization inside virtual scroller.

One solution is memoization withuseMemo. You can use it to reduce computation and keep the elements' instance the same. And if you want to pass state from parent to the items, usingcontext instead of props may be better because it doesn't break the memoization.

constelements=useMemo(()=>tooLongArray.map((d)=><Componentkey={d.id}{...d}/>),[tooLongArray]);const[position,setPosition]=useState(0);return(<div><div>position:{position}</div><VListonScroll={(offset)=>setPosition(offset)}>{elements}</VList></div>);

The other solution is usingrender prop as children to create elements lazily. It will effectively reduce cost on start up when you render many items (>1000). An important point is that newly created elements fromrender prop will disableoptimization possible with cached element instances. We recommend usingmemo to reduce calling render function of your item components during scrolling.

constComponent=memo(HeavyItem);<VListcount={items.length}>{(i)=>{constitem=items[i];return<Componentkey={item.id}data={item}/>;}}</VList>;

Decreasingoverscan prop may also improve perf in case that components are large and heavy.

Virtua try to suppress glitch caused by resize as much as possible, but it will also require additional work. If your item contains something resized often, such as lazy loaded image, we recommend to set height or min-height to it if possible.

What isResizeObserver loop completed with undelivered notifications. error?

It may be dispatched by ResizeObserver in this libas described in spec, andthis is a common problem with ResizeObserver. If it bothers you,you can safely ignore it.

Especially forwebpack-dev-server,you can filter out the specific error withdevServer.client.overlay.runtimeErrors option.

Why my items are squashed on resize/add/remove?

Maybe you forgot to passkey prop to each items, or the keys are not unique. Item sizes are stored per key.

WhyVListHandle.viewportSize is 0 on mount?

viewportSize will be calculated by ResizeObserver so it's 0 until the first measurement.

What isCannot find module 'virtua/vue(solid|svelte)' or its corresponding type declarations error?

This package usesexports of package.json for entry point of Vue/Solid/Svelte adapter. This field can't be resolved in TypeScript withmoduleResolution: node. TrymoduleResolution: bundler ormoduleResolution: nodenext instead.

Comparison

Features

virtuareact-virtuosoreact-windowreact-virtualized@tanstack/react-virtualreact-tiny-virtual-listreact-cool-virtual
Bundle sizenpm bundle sizenpm bundle sizenpm bundle sizenpm bundle sizenpm bundle sizenpm bundle sizenpm bundle size
Vertical scroll🟠 (needs customization)🟠 (needs customization)
Horizontal scroll✅ (may be dropped in v2)🟠 (needs customization)🟠 (needs customization)
Horizontal scroll in RTL direction✅ (may be dropped in v2)
Grid (Virtualization for two dimension)🟠 (experimental_VGrid)✅ (FixedSizeGrid / VariableSizeGrid)✅ (Grid)🟠 (needs customization)🟠 (needs customization)
Table🟠 (needs customization)✅ (TableVirtuoso)🟠 (needs customization)🟠 (Table but it's built with div)🟠 (needs customization)🟠 (needs customization)
Window scroller✅ (WindowVirtualizer)✅ (WindowScroller)✅ (useWindowVirtualizer)
Dynamic list size🟠 (needsAutoSizer)🟠 (needsAutoSizer)
Dynamic item size🟠 (needs additional codes and has wrong destination when scrolling to item imperatively)🟠 (needsCellMeasurer and has wrong destination when scrolling to item imperatively)🟠 (has wrong destination when scrolling to item imperatively)🟠 (has wrong destination when scrolling to item imperatively)
Reverse scroll
Reverse scroll in iOS Safari🟠 (user must release scroll)🟠 (has glitch with unknown sized items)
Infinite scroll🟠 (needsreact-window-infinite-loader)🟠 (needsInfiniteLoader)
Reverse (bi-directional) infinite scroll🟠 (has startItem method but its scroll position can be inaccurate)
Scroll restoration✅ (getState)
Smooth scroll
SSR support
Render React Server Components (RSC) as children🟠(needs customization)🟠 (needs customization)
Display exceedingbrowser's max element size limit
  • ✅ - Built-in supported
  • 🟠 - Supported but partial, limited or requires some user custom code
  • ❌ - Not officially supported

Benchmark

WIP

Contribute

All contributions are welcome.If you find a problem, feel free to create anissue or aPR. If you have a question, ask indiscussions.

Making a Pull Request

  1. Fork this repo.
  2. Runnpm install.
  3. Commit your fix.
  4. Make a PR and confirm all the CI checks passed.

[8]ページ先頭

©2009-2025 Movatter.jp