Behavioral
easy
mid
Where have you actually used virtualization in your projects?
Virtualization renders only the visible portion of a large list/grid, drastically cutting DOM nodes. Real uses: chat message history, data tables with 10k+ rows, file explorers, feed/timeline UIs, log viewers, code editors. Libraries: TanStack Virtual, react-window, react-virtuoso. The win is DOM size, not render speed per row.
7 min read·~5 min to think through
Pick a concrete project, name the user-visible problem, then describe the fix.
Example narrative
On the admin dashboard, we had a customer-list page that loaded 25,000 rows. The initial render took ~4 seconds and scrolling was janky. We swapped the table for react-window's FixedSizeList. After: initial render under 200ms, smooth scrolling at 60fps. DOM dropped from 25k rows to ~30 visible + buffer.
That's a complete answer: problem, intervention, measured outcome.
Common places to virtualize
| Where | Typical size | Why it matters |
|---|---|---|
| Chat history | 1000s of messages | Keeps scrollback responsive |
| Data tables (CRM, admin) | 10k–100k rows | Pagination is worse UX for power users |
| File trees / Finder | 50k+ files | Filesystem dumps |
| Feed / timeline | infinite | Reverse-chrono streams |
| Log viewers | unbounded | Tail or jump-to-line |
| Code editor / diff | thousands of lines | Monaco/CodeMirror have it built in |
| Autocomplete | 10k items | Searching big catalogs |
| Dropdown / select | 1k+ options | Country pickers, taxonomy |
| Calendar grid | 365+ days | Sparse rendering |
Libraries
- TanStack Virtual — modern, headless, supports dynamic heights, recommended.
- react-window — established lightweight option.
- react-virtuoso — chat-friendly, sticky-headers, auto-scroll on append.
- AG Grid / TanStack Table — full data-grid solutions with virtualization built in.
Basic example
tsx
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={600}
itemCount={rows.length}
itemSize={48}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<Row item={rows[index]} />
</div>
)}
</FixedSizeList>When virtualization is the wrong fix
- List has < 500 items: just memoize the row.
- Variable + unknown heights: dynamic measurement adds complexity; sometimes pagination is simpler.
- SEO matters: virtualization hides content from crawlers — SSR with explicit pagination is safer.
- Items animate in/out heavily: keeping things in DOM is sometimes cheaper than mounting/unmounting on scroll.
Things that bite
- Sticky headers / horizontal scroll: easy to get wrong; library matters.
- Scroll-to-item: needs an imperative API (scrollToIndex).
- Selection across off-screen rows: keep selection in state, not in DOM-only.
- Find-in-page (Ctrl+F): only finds rendered text — surprising for users.
- Focus management: tabbing into off-screen rows is undefined; many libraries fall back gracefully.
Where I've used it for real
- Slack-like chat — react-virtuoso for reverse-chrono message list with auto-scroll-on-append.
- Customer admin table — react-window + TanStack Table for sortable, filterable 50k rows.
- File picker — TanStack Virtual for a flat directory listing of cloud storage.
- Log viewer — react-window for unbounded log streams with sticky-to-bottom + jump-to-line.
Measuring
Always measure before/after: DOM size, time to interactive, scrolling frame rate, memory footprint.
Follow-up questions
- •How do you handle variable row heights in a virtualized list?
- •What breaks for users when you virtualize?
- •When would you NOT virtualize?
Common mistakes
- •Virtualizing a list of 50 items — overhead is bigger than the gain.
- •Forgetting scrollToIndex when the list needs imperative scroll.
- •Losing in-DOM state of rows as they unmount on scroll.
Performance considerations
- •The win is DOM size, not render speed per row. A list of 10k rows mounted in a virtual scroller renders ~30 rows of DOM at a time; the browser has 100× less to layout. Pair with row memoization for max benefit on scroll.
Edge cases
- •Dynamic heights need measurement-then-render or estimated sizes that settle on first measure.
- •Horizontal + vertical virtualization (data grids) compounds complexity.
- •Rendering inside iframes or shadow DOM can confuse virtualization libraries.
Real-world examples
- •Slack messages, Gmail inbox, GitHub issues lists, VSCode's file tree, Notion's database views, Linear's issue list — all virtualized. Table stakes for any app with large lists.
Senior engineer discussion
Senior framing: virtualization isn't about React perf — it's about not asking the browser to lay out and paint thousands of nodes. That problem exists in any framework. Knowing when to reach for it (and when pagination is the right answer instead) is the senior signal.