Layout
Virtual list
Renders only the rows in view, for very long lists.
import { VirtualList } from '@jsonresume/ui'Source(opens in a new tab)
Examples
Show code
virtual-list/log.tsx
'use client';
import { Text, VirtualList } from '@jsonresume/ui';
import * as styles from './log.css';
const events = Array.from({ length: 5000 }, (_, i) => ({
id: i,
text: `Matched job #${5000 - i} against your resume`,
score: 40 + ((i * 37) % 60),
}));
// 5,000 rows; only the visible ones are in the DOM.
export default function VirtualListLog() {
return (
<div className={styles.frame}>
<VirtualList
label="Match log"
height="18rem"
estimateSize={44}
items={events}
getKey={(e) => e.id}
renderItem={(e) => (
<div className={styles.row}>
<Text size="sm">{e.text}</Text>
<Text size="xs" tone="muted">
{e.score}% match
</Text>
</div>
)}
/>
</div>
);
}virtual-list/log.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from '@jsonresume/ui/tokens';
export const frame = style({
border: `1px solid ${vars.color.border.default}`,
borderRadius: vars.radius.sm,
backgroundColor: vars.color.surface.raised,
});
export const row = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: vars.space[3],
padding: `${vars.space[3]} ${vars.space[4]}`,
borderBottom: `1px solid ${vars.color.border.subtle}`,
});Accessibility
- Rows keep aria-setsize and aria-posinset so their position in the full list is known.
- The scroll region is focusable and labelled.
Props
<VirtualList>
| Prop | Type | Default | Description |
|---|---|---|---|
| height required | string | – | Visible height (any CSS length). |
| items required | T[] | – | |
| label required | string | – | |
| renderItem required | (item: T, index: number) => ReactNode | – | Renders one item; keep row heights close to estimateSize. |
| estimateSize | number | 56 | Estimated row height in px; real heights are measured. |
| getKey | ((item: T, index: number) => string | number) | – |