Krizaka
Documentation

Virtual

Long lists and galleries rendered by what is in view, on TanStack Virtual: a list in its own region or the page's, a grid that fits the width, measured items, the end announced for infinite loading.

WebBetaData display

Web only — React, from @krizaka/ui/virtual. Beta: its API may still change in a minor version.

When to use

  • A list of hundreds to millions of items loaded at once or page by page: messages, comments, logs, notifications.
  • A gallery that grows as the person scrolls: videos, photos, products (VirtualGrid).
  • A feed that scrolls the page itself rather than a box (scroll="window").

When not to use

  • For records compared on columns: the data table has its own windowing.Use instead: Data table
  • For a short list (under a hundred items): render it whole, with no scrolling region.Use instead: Card grid
  • In a React Native app: FlashList, through the native list.Use instead: List

Installation

Install

npm install @krizaka/ui @krizaka/tailwind @krizaka/tokens tailwindcss

Styles

@import "tailwindcss";
@import "@krizaka/tailwind";
@import "@krizaka/ui/tailwind.css";

Import

import { VirtualList, VirtualGrid, useWindowVirtualizer, useVirtualizer } from "@krizaka/ui/virtual";

Examples

50,000 messages

A VirtualList of items of different heights, each measured.

Noah

On my way.

Elena

Can you send the cut from yesterday? The second take, with the sunset over the river and the drone shot at the end.

Noah

Done!

Elena

Thanks — looks great, posting it tonight at eight. I'll tag you.

Noah

On my way.

Elena

Can you send the cut from yesterday? The second take, with the sunset over the river and the drone shot at the end.

Noah

Done!

Elena

Thanks — looks great, posting it tonight at eight. I'll tag you.

Noah

On my way.

Elena

Can you send the cut from yesterday? The second take, with the sunset over the river and the drone shot at the end.

Noah

Done!

Elena

Thanks — looks great, posting it tonight at eight. I'll tag you.

A VirtualGrid of 50,000 clips: as many columns as the width allows.

Clip 1

1 min

Clip 2

2 min

Clip 3

3 min

Clip 4

4 min

Clip 5

5 min

Clip 6

6 min

A feed

scroll="window": the page scrolls, 20 more posts each time the end comes near.

Post 1
Post 2
Post 3
Post 4
Post 5
Post 6
Post 7
Post 8
Post 9
Post 10
Post 11
Post 12
Post 13
Post 14
Post 15
Post 16
Post 17

Props

VirtualGrid

A gallery rendered by the rows in view: as many columns as the width allows (or a fixed number), measured rows.

PropTypeDefaultDescription
estimateRowHeightrequirednumber—The height of a row in px (a guess: rows are measured).
itemsrequiredreadonly T[]—The items, all of them: only the rows in view are rendered.
labelrequiredstring—The accessible name of the grid of items ("Videos"), passed translated.
renderItemrequired(item: T, index: number) => ReactNode—Draws one cell (a card, a thumbnail).
classNamestring—Classes merged on the scrolling region.
columnsnumber—A fixed number of columns. Without it, as many columns of at least minItemWidth as the width allows.
emptyReactNode—Shown instead of the grid when there is no item.
footerReactNode—Shown after the last row.
gapnumber16Space between cells, in px. Default 16.
getKey((item: T, index: number) => string | number)—A stable key per item. Default: the index.
heightstring | number480The height of the scrolling region: px or any CSS length. Default 480.
minItemWidthnumber220The narrowest a cell may be, in px, when columns is not set. Default 220.
onEndReached(() => void)—Called when the last rows come near (load the next page).
overscannumber3Rows rendered beyond the visible ones. Default 3.
refRef<VirtualHandle>—scrollToIndex (of an item) from outside.

VirtualList

A long list rendered by what is in view: fixed or measured sizes, its own scrolling region or the page's, the end announced for infinite loading, items placed in the whole for assistive technology.

PropTypeDefaultDescription
estimateSizerequiredSize—The size of an item in px, or a guess per index; each item is then measured (dynamic sizes).
itemsrequiredreadonly T[]—The items, all of them: only those in view (and overscan around) are rendered.
labelrequiredstring—The accessible name of the list ("Messages", "Events"), passed translated.
renderItemrequired(item: T, index: number) => ReactNode—Draws one item.
classNamestring—Classes merged on the scrolling region (self) or the list (window).
emptyReactNode—Shown instead of the list when there is no item.
endReachedThresholdnumber5How many items before the end onEndReached fires. Default 5.
footerReactNode—Shown after the last item (a spinner while the next page loads).
gapnumber0Space between two items, in px. Default 0.
getKey((item: T, index: number) => string | number)—A stable key per item (an id): keeps measurements on the right item when the list changes. Default: the index.
heightstring | number400The height of the scrolling region (self): px or any CSS length. Default 400.
onEndReached(() => void)—Called when the end comes near (load the next page): once per length of items.
overscannumber6Items rendered beyond the visible ones, on each side. Default 6.
refRef<VirtualHandle>—scrollToIndex from outside.
scroll"self" | "window"selfself (default): the list scrolls in its own region of height. window: the page scrolls (a feed).

Accessibility

  • The scrolling region is focusable (tabIndex=0) and named by label.
  • Items are a role="list" of listitems carrying aria-setsize and aria-posinset: a screen reader says “item 4,812 of 50,000” though only twenty exist in the page.
  • scrollToIndex (through ref) scrolls smoothly, or at once when the person asked for less motion.

Keyboard

KeysAction
TabReaches the scrolling region (then the links and buttons of the items in view).
Arrow keysScroll the focused region; Page Up / Page Down and Home / End move by a screen or to the ends.

Best practices

  • Give getKey (an id): measurements stay with their item when the list changes.
  • Estimate the size close to the average (estimateSize): items are measured anyway, a close guess keeps the scrollbar steady.
  • Load the next page in onEndReached (a useInfiniteQuery of @krizaka/query) and show a spinner in footer.
  • Name the list (label): it names the focusable scrolling region, so keyboard people can scroll it.
  • Need the raw engine? useVirtualizer and useWindowVirtualizer come through this entry — never import @tanstack/* in a product.

Generated from the code of @krizaka/ui 3.0.0: its meta.ts, its examples and its types.Edit this documentation on GitHub

On this page