Big lists, cheap keystrokes
Render only visible rows and keep each keystroke under a frame.
Web Interface Guidelinesvercel-labs · command.md
- Large lists (>50 items): virtualize (`virtua`, `content-visibility: auto`) - No layout reads in render (`getBoundingClientRect`, `offsetHeight`, `offsetWidth`, `scrollTop`) - Batch DOM reads/writes; avoid interleaving - Prefer uncontrolled inputs; controlled inputs must be cheap per keystroke
Web Interface Guidelinesvercel-labs · command.md
- Large arrays `.map()` without virtualization
Web Interface Guidelinesvercel-labs · command.md
- Flex/grid over JS measurement for layout
virtuainokawa · GitHub
A zero-config, fast and small virtual list and grid component for React, Vue, Solid, Svelte and Angular.
From web-design-guidelines (Web Interface Guidelines).
Mount a long list
Remount, then toggle Dense rows. Compare the costs.
.map() every rowBad- Rows in DOM
- –
- Mount
- –
- Relayout
- –
content-visibilityCheaper- Rows in DOM
- –
- Mount
- –
- Relayout
- –
Virtualized windowGood
- Rows in DOM
- –
- Mount
- –
- Relayout
- –
const start = Math.floor(viewport.scrollTop / ROW_H) - OVERSCAN;
const end = start + Math.ceil(viewH / ROW_H) + OVERSCAN * 2;
.row { content-visibility: auto; contain-intrinsic-size: auto 32px; }
Filter while typing
Type fast in each box, or press Type for me.
Renders every matchBad
- Last key
- –
- Worst
- –
- Over 50 ms
- –
- Matches
- –
Uncontrolled, windowedGood
- Last key
- –
- Worst
- –
- Over 50 ms
- –
- Matches
- –
<input type="search" defaultValue="" onInput={onQuery} />
const q = useDeferredValue(query);
Read, then write
Press Resize all, then raise the box count.
InterleavedBad
- Layout passes
- –
- Time to paint
- –
BatchedGood
- Layout passes
- –
- Time to paint
- –
const widths = boxes.map((b) => b.offsetWidth);
boxes.forEach((b, i) => (b.style.width = `${next(widths[i])}px`));
Why it matters, pros and cons
Why it matters
Every DOM node costs layout, yet readers see about twenty rows.
Pros
- Mount cost stays flat at any size.
- Keystrokes paint within a frame.
- N forced layouts become one.
Cons
- Find-in-page and screen readers see only the window.
- Variable row heights need measuring or a library.
- Under ~50 rows it is pure overhead.