# useVirtualList

Render only the items of a long list that are in or near view, for Thread's and SessionList's `virtualize`. Items keep their measured heights by key, so a list that grows or reorders keeps its place; unmeasured items count as `estimate`. Spacers stand in for the rest. `pin` (an item's key) keeps that item rendered (the one holding focus), on its own if it's far from view, so focus is never lost to an item scrolling away. Until the scroller is measured (on the server, on the first render in the browser, and while it's hidden) the first or last `initial` items render, by `anchor`, so server and browser agree.

Give each rendered item `ref={list.measure}` and `data-virtual-key={key}`.

- Registry name: `use-virtual-list` (`@candor/use-virtual-list`)
- Tier: Free (MIT). No licence key needed.
- Docs: https://candor.themethodic.com/docs/use-virtual-list

## Install

```bash
npx candor-ui add use-virtual-list
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/use-virtual-list`.

## Functions

### useVirtualList

```ts
useVirtualList({ enabled, count, keyOf, scroller, estimate = 96, overscan = 800, initial = 20, anchor = "end", gap = () => 0, pin, }: { enabled: boolean count: number keyOf: (index: number) => React.Key scroller: React.RefObject<HTMLElement | null> estimate?: number | undefined overscan?: number | u
```

Render only the items of a long list that are in or near view, for Thread's and SessionList's `virtualize`. Items keep their measured heights by key, so a list that grows or reorders keeps its place; unmeasured items count as `estimate`. Spacers stand in for the rest. `pin` (an item's key) keeps that item rendered (the one holding focus), on its own if it's far from view, so focus is never lost to an item scrolling away. Until the scroller is measured (on the server, on the first render in the browser, and while it's hidden) the first or last `initial` items render, by `anchor`, so server and browser agree.

Give each rendered item `ref={list.measure}` and `data-virtual-key={key}`.

## Types

### VirtualRow

What to render, in order: an item by its index, or a spacer standing in for the items between.

```ts
{ index: number } | { spacer: number; key: string }
```

### VirtualList

```ts
{ /** The items to render and the spacers between them, in order. */ rows: VirtualRow[] /** The ref for every rendered item, which also carries `data-virtual-key={key}`, so its real height is measured and kept. */ measure: (el: HTMLElement | null) => void | (() => void) }
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
