30% offEnding soon
FSD-016Frontend system design Premium

Design a Variable-Height Virtual List

Design a virtual list that measures dynamic rows, preserves scroll position, bounds overscan, retains focus, and remains accessible.

Intermediate 54 min read

A virtual list does not shrink the logical collection. It preserves one large scroll surface while mounting only the rows near the viewport.

Fixed-height windowing is mostly arithmetic. Divide scrollTop by the row height, add overscan, and position the slice. Variable heights remove that shortcut. A title wraps after resize, an image decodes, or an expanded card changes the size of every offset below it. If the size index and viewport anchor are not updated together, the reader sees a jump.

The central data structure is not the rendered row array. It is a size index keyed by stable item identity, plus a way to answer two questions quickly: which row contains this scroll offset, and what offset belongs to this row?

Start with one coordinate system

Treat the collection as an ordered sequence of stable IDs. Each ID has an estimated or measured height. Their accumulated heights define the virtual surface. The renderer mounts a visible range plus bounded overscan and positions it inside that surface.

The same size index must power range lookup, row positioning, total height, programmatic reveal, and restoration. If those operations use different estimates, the scrollbar and rendered rows disagree.

The public interview prompt

Design a variable-height virtual list for messages, search results, or activity cards. Explain:

  • the logical collection, stable IDs, size estimates, measurements, and prefix offsets;
  • mapping scroll offsets to row indices and row indices back to offsets;
  • visible range, pixel and row overscan, scroll direction, and fast jumps;
  • ResizeObserver batching, image decode, font changes, expansion, and resize;
  • anchor selection and correction when heights change above the viewport;
  • insertion, deletion, reordering, filtering, and measurement invalidation;
  • focus pinning, keyboard navigation, selection, find, screen readers, and semantics;
  • programmatic scrollToItem, deep links, restoration, and missing items;
  • server rendering, hydration, printing, no-script behavior, and SEO;
  • memory, main-thread budgets, testing, observability, rollout, and fallbacks.

Assume 100,000 logical items, typical rows between 48 and 320 pixels, embedded media, keyboard navigation, and a mid-range mobile target. These values are interview assumptions, not measured production facts.

What the premium solution covers

The full solution builds the measurement and range model, compares prefix arrays with tree-based indexes, defines batched measurement correction, keeps focused rows alive, and draws a clear boundary between pagination and windowing.

Original media follows a late row resize through one stable anchor correction, maps the logical surface and mounted window, opens the measurement index, and shows focus-safe recycling. Two deterministic labs calculate variable ranges and reveal exactly when a measured delta should move scrollTop.

Premium solution

Continue with the complete system design

Unlock the architecture, state machine, API contracts, original diagrams, positioning model, accessibility decisions, performance budgets, testing plan, rollout strategy, scoring rubric, and interview walkthrough.

  • Detailed, beginner-friendly explanation
  • Production failure modes and trade-offs
  • Mobile-friendly architecture diagrams
  • Senior and staff-level interview signals
Unlock Premium

Frequently asked questions

When should a list be virtualized?
Virtualize after measurement shows that retained DOM size, rendering, or memory harms target devices. A short list is usually better with normal document flow because it is simpler to render, search, print, and navigate.
How can a virtual list handle unknown row heights?
Start with a reasonable estimate by row type, mount a bounded window, measure actual box sizes, update a size index, and reposition later rows. Preserve one stable visible anchor when changes occur above it.
What should be used as a row key?
Use a stable item ID. The current array index may change after insertion, filtering, or sorting, so it cannot safely own measurement, focus, selection, or component state.
How much overscan should a virtual list render?
Keep a small minimum on both sides, then bias modestly toward the scroll direction if measured rendering can keep up. Bound it by row count, pixels, and render cost rather than using an unlimited buffer.
How does keyboard focus work when rows are recycled?
Never unmount the element that contains DOM focus. Pin it temporarily, or scroll the target into the mounted range before moving focus. Use stable IDs to reconnect logical focus after range changes.
Is role feed required for a virtualized list?
No. Use native list or article semantics by default. The feed pattern is a specific accessibility contract for a dynamic stream of articles. A selectable option collection may instead use listbox semantics with the required keyboard model.