A zero-config, fast and small (~3kB) virtual list and grid component for React.
If you want to check the difference with the alternatives right away, see comparison section.
This project is a challenge to rethink virtualization. The goals are...
- Zero configuration: 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 in bottom-up scrolling and imperative scrolling, etc).
- Fast: Scrolling without frame drop needs optimization in many aspects (reduce CPU usage, reduce GC, reduce layout recalculation, 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 the total is ~4kB gzipped.
- Flexible: Aiming to support many usecases - fixed size, dynamic size, horizontal scrolling, reverse scrolling, rtl direction, sticky, infinite scrolling, placeholder, scrollTo, dnd, table, and more. See live demo.
- Framework agnostic (WIP): Currently only for React but we could support Vue, Svelte, Solid, Angular, Web Components and more in the future.
https://inokawa.github.io/virtua/
npm install virtua
- react >= 16.14
If you use ESM and webpack 5, use react >= 18 to avoid Can't resolve react/jsx-runtime
error.
If you use this lib in legacy browsers which does not have ResizeObserver, you should use polyfill.
import { VList } from "virtua";
export const App = () => {
return (
<VList style={{ height: 800 }}>
{Array.from({ length: 1000 }).map((_, i) => (
<div
key={i}
style={{
height: Math.floor(Math.random() * 10) * 10 + 10,
borderBottom: "solid 1px gray",
background: "white",
}}
>
{i}
</div>
))}
</VList>
);
};
import { VList } from "virtua";
export const App = () => {
return (
<VList style={{ height: 400 }} horizontal>
{Array.from({ length: 1000 }).map((_, i) => (
<div
key={i}
style={{
width: Math.floor(Math.random() * 10) * 10 + 10,
borderRight: "solid 1px gray",
background: "white",
}}
>
{i}
</div>
))}
</VList>
);
};
import { VGrid } from "virtua";
export const App = () => {
return (
<VGrid style={{ height: 800 }} row={1000} col={500}>
{({ rowIndex, colIndex }) => (
<div
style={{
width: ((colIndex % 3) + 1) * 100,
border: "solid 1px gray",
background: "white",
}}
>
{rowIndex} / {colIndex}
</div>
)}
</VGrid>
);
};
This library is marked as a Client Component. You can render RSC as children of VList.
// page.tsx in App Router of Next.js
import { VList } from "virtua";
export default async () => {
return (
<div>
<div>This is Server Component</div>
<VList style={{ height: 300 }}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i} style={{ border: "solid 1px gray", height: 80 }}>
{i}
</div>
))}
</VList>
</div>
);
};
And see examples for more usages.
WIP
virtua | react-virtuoso | react-window | react-virtualized | @tanstack/react-virtual | react-cool-virtual | |
---|---|---|---|---|---|---|
Bundle size | 4.1kB gzipped | 16.3kB gzipped | 6.4kB gzipped | 27.3kB gzipped | 2.3kB gzipped | 3.1kB gzipped |
Vertical scroll | β | β | β | β | π (needs customization) | π (needs customization) |
Horizontal scroll | β | β | β | β | π (needs customization) | π (needs customization) |
Grid (Virtualization for two dimension) | β | β | β (FixedSizeGrid / VariableSizeGrid) | β (Grid) | π (needs customization) | π (needs customization) |
Table | π (needs customization) | β (TableVirtuoso) | π (needs customization) | β (Table) | π (needs customization) | π (needs customization) |
Window scroller | β | β | β | β (WindowScroller) | β | β |
Dynamic list size | β | β | π (needs AutoSizer) | π (needs AutoSizer) | β | β |
Dynamic item size | β | β | π (needs additional codes and has wrong destination when scrolling to item imperatively) | π (needs CellMeasurer 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 | β | β | β | β | β | β |
Infinite scroll | β | β | π (needs react-window-infinite-loader) | π (needs InfiniteLoader) | β | β |
RTL | β | β | β | β | β | β |
SSR support | β | β | β | β | β | β |
React Server Components (RSC) support | β | ? | ? | ? | β (hook) | β (hook) |
Display exceeding browser's max element size limit | β | β | β | β | β | β |
- β - Built-in supported
- π - Supported but partial, limited or requires some user custom code
- β - Not officially supported
All contributions are welcome. If you find a problem, feel free to create an issue or a PR.
- Clone this repo.
- Run
npm install
. - Commit your fix.
- Make a PR and confirm all the CI checks passed.