Skip to content
Frontend
Skill

/react-virtuoso

Build virtualized lists, grids, and tables with react-virtuoso. Use this skill when (1) rendering large or infinite lists, (2) building grouped lists with sticky headers, (3) virtualizing HTML tables, (4) laying out same-sized items in a responsive grid, (5) building feeds or

BOOST
From plugin
react-virtuoso
6.5k4 skills
Install
$ npx -y skills add petyosi/react-virtuoso --skill react-virtuoso --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/react-virtuoso

Context preview

The summary Claude sees to decide when to auto-load this skill.

Build virtualized lists, grids, and tables with react-virtuoso. Use this skill when (1) rendering large or infinite lists, (2) building grouped lists with sticky headers, (3) virtualizing HTML tables, (4) laying out same-sized items in a responsive grid, (5) building feeds or

SKILL.md

react-virtuoso.SKILL.md
name: react-virtuoso
description: >-
  Build virtualized lists, grids, and tables with react-virtuoso. Use this skill when (1) rendering large or infinite lists,
  (2) building grouped lists with sticky headers, (3) virtualizing HTML tables, (4) laying out same-sized items in a responsive grid,
  (5) building feeds or logs that follow new items at the bottom, (6) diagnosing virtualization symptoms such as jumpy scrolling,
  a list that does not scroll to the bottom, items overlapping, blank items, or "zero-sized element" errors, or (7) any task involving
  Virtuoso, GroupedVirtuoso, VirtuosoGrid, TableVirtuoso, VirtuosoHandle, itemContent, followOutput, or firstItemIndex.

react-virtuoso

`react-virtuoso` renders only the visible portion of large lists, grids, and tables. It measures item sizes automatically with ResizeObserver — variable item heights work out of the box, with no size configuration.

import { Virtuoso } from 'react-virtuoso'
;<Virtuoso style={{ height: '100%' }} data={users} itemContent={(index, user) => <div>{user.name}</div>} />

Picking the right component

| Need | Use | | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | | Flat list, variable or fixed item heights | `Virtuoso` | | Groups with sticky group headers | `GroupedVirtuoso` | | HTML table with virtualized rows | `TableVirtuoso` | | Table with grouped rows and sticky group headers | `GroupedTableVirtuoso` | | Same-sized items in a responsive multi-column grid | `VirtuosoGrid` | | Chat / AI conversation UI (streaming, stick-to-bottom, prepend history) | `@virtuoso.dev/message-list` — use the `message-list` skill instead | | Data grid with columns, sorting, filtering, column features | `@virtuoso.dev/data-table` — use the `data-table` skill instead |

All components share the same core props (`data`/`totalCount`, `itemContent`, `components`, scroll callbacks, ref methods).

What you do NOT need to do

Unlike TanStack Virtual or react-window, react-virtuoso measures items itself. Do not carry those libraries' patterns over:

  • No `estimateSize`, no `measureElement`, no `data-index` wiring — measurement is automatic.
  • No absolute positioning or `transform: translateY` on items — the library positions items.
  • No fixed `itemSize` requirement — variable heights are the default. If items genuinely have one uniform height, pass `fixedItemHeight` as a performance optimization only.
  • No windowing math — pass `data` (or `totalCount`) and render the item in `itemContent`.

Core rules

  • **The component needs a height.** Set `style={{ height: '100%' }}` (with a sized parent) or a fixed height. A zero-height container renders nothing.
  • **Never put CSS margins on items.** ResizeObserver reports `contentRect`, which excludes margins, so the computed total height comes up short — the classic symptom is a list that cannot scroll all the way to the bottom. Use padding instead. Watch for default margins on `<p>`, headings, `<ul>`, `<blockquote>`, `<pre>`.
  • **Use `data`, not `totalCount`, when you have the items.** With `data`, `itemContent={(index, item) => ...}` receives the item. Use `totalCount` only when items are derived from the index. Updates must produce a new array reference.
  • **Provide `computeItemKey={(index, item) => item.id}`** whenever the list can be prepended, reordered, or filtered. The default key is the index, which remounts items (losing state) when positions shift.
  • **Define `components` overrides outside the render function.** Inline definitions create a new component type each render, remounting the subtree on every scroll. `Scroller` and `List` overrides must forward `ref` to their DOM element.
  • **Item content must not render zero-height elements.** The error "zero-sized element, this should not happen" means an item measured 0px — filter empty items out of the data instead.

Common patterns

Infinite scrolling

<Virtuoso data={items} endReached={() => loadMore()} itemContent={(index, item) => <Item item={item} />} />

`endReached` fires at the bottom; render a spinner via `components.Footer`. For "load more" on click, put the button in `Footer`.

Prepending items (reverse infinite scroll)

Prepending naively makes the list jump. Instead, keep a `firstItemIndex` that you decrease by the number of prepended items:

const [firstItemIndex, setFirstItemIndex] = useState(START)

const prepend = async () => {
  const older = await fetchOlderItems()
  setFirstItemIndex((i) => i - older.length)
  setItems((current) => [...older, ...current])
}

<Virtuoso
  computeItemKey={(_, item) => item.id}
  data={items}
  firstItemIndex={firstItemIndex}
  initialTopMostItemIndex={{ index: 'LAST' }}
  startReached={() => void prepend()}
  ...
/>

`firstItemIndex` must stay a positive number, so start it large (e.g. `100000`). For `GroupedVirtuoso`, decrease it by the number of new items only, excluding the group headers. See [endless-scrolling](references/1.virtuoso/endless-scrolling.md).

Following new items (logs, feeds)

<Virtuoso followOutput="smooth" data={messages} ... />

`followOutput` scrolls to new bottom items only when the user is already at the bottom. It accepts `'auto' | 'smooth' | false` or a function `(isAtBottom) => ...` for custom logic. For full chat UIs prefer `@virtuoso.dev/message-

Read more
Ships withreact-virtuoso

The most complete React virtualization rendering family of components. Variable sized items out of the box; no manual measurements or hard-coding item heights is necessary; Chat message list UI; Grouped mode with sticky headers; Responsive grid layout;

Get the whole plugin
Stats
6,464
Stars
357
Forks
Active
Maintenance
TypeScript
Language
6d ago
Last commit
7y ago
Created
16h ago
Added

Repo: petyosi/react-virtuoso

Other skills on react-virtuoso.