Long lists
Use Virtual Scroll when a list is large enough that rendering every row at once would add unnecessary DOM weight and scrolling cost.
Icons by @ng-icons/tabler-icons
The component library does not provide icons.
Reusable fixed-row virtualization primitives for large data sets.
Preview
TS
import { FrVirtualScrollModule } from '@frame-ui-ng/components/virtual-scroll';
items = Array.from({ length: 500 }, (_, index) => ({
id: index,
label: `Release note ${index + 1}`,
meta: `v${Math.floor(index / 10) + 1}.${(index % 10) + 1}`,
}));
trackItem = (_index: number, item: { id: number }) => item.id;HTML
<div frVirtualList>
<div frVirtualViewport [height]="'18rem'" [itemSize]="52" [overscan]="4">
<div frVirtualContent>
<button frVirtualItem *frVirtualFor="let item of items; trackBy: trackItem">
<span>{{ item.label }}</span>
</button>
</div>
</div>
</div>Use Virtual Scroll when a list is large enough that rendering every row at once would add unnecessary DOM weight and scrolling cost.
The viewport instance exposes `scrollToIndex`, which is useful for jump navigation, keyboard shortcuts, and preserving context after filtering.
Virtual Scroll works well inside Select-like option panels when the option set is large but the visual treatment should still read like a familiar picker.
Virtual Scroll also fits Combobox-style search results, where the result set can spike but the panel still needs to feel immediate and keyboard-friendly.
Virtual Scroll does not define dedicated `--frame-virtual-scroll-*` tokens. Customize it by overriding shared surface tokens locally or by targeting the helper classes for row density and spacing.
Inspect the list shell, viewport, content padding, item rows, and meta text. Virtual Scroll is intentionally structural, so most of its styling comes from inherited surface tokens plus the helper classes.
Virtual Scroll is structural and does not define its own component-scoped token contract. It inherits shared surface, border, radius, accent, and muted-text tokens from the surrounding FrameUI.