Skip to content
/ virtua Public

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

License

Notifications You must be signed in to change notification settings

inokawa/virtua

Repository files navigation

virtua

npm npm bundle size npm check demo

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.

Motivation

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.

Demo

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

Install

npm install virtua

Requirements

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

Usage

Vertical scroll

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

Horizontal scroll

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

Vertical and horizontal scroll

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

React Server Components (RSC) support

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.

Documentation

Comparison

Benchmarks

WIP

Features

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

Contribute

All contributions are welcome. If you find a problem, feel free to create an issue or a PR.

Making a Pull Request

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