Performance
Cheat-sheet · 4 min · May 2026
Long lists: the decision guide
The scannable companion to Virtualization vs. Infinite Scroll. No narrative. Tables and rules. Keep it in a second tab.
The three-layer model
Virtualization, infinite scroll, and pagination aren't a menu. They answer three different questions, at three layers. A serious feed uses all three.
1
Fetch
How does the server slice the data?
Pagination — offset vs. cursor
2
Trigger
When does the client ask for the next slice?
Infinite scroll
3
Render
How much of it actually goes in the DOM?
Virtualization
When to use what
Two questions decide it. Is the data already in memory? Does the user scroll deep?
Data in memory
Unbounded / on the server
Shallow scroll
Just render it.
Infinite scroll alone.
Deep scroll
Virtualization alone.
Infinite scroll + virtualization.
Offset vs. cursor pagination
The Layer 1 decision. The short version: infinite scroll needs cursor pagination. Offset breaks correctness the moment the list shifts.
Offset
Cursor
Deep-page speed
Degrades linearly. ~17x slower deep.
Constant at any depth. Indexed lookup.
Stable under inserts / deletes
No. Rows duplicate or vanish.
Yes. The cursor points at a real row.
Jump to an arbitrary page
Yes.
No. Sequential only.
Total count / “page 5 of 99”
Yes.
No. The server never counted.
Best for
Numbered pages, admin tables, static data.
Infinite scroll, live feeds, shifting data.
Rules of thumb
- →Under ~100 rows: render it. No tooling. Virtualization is overkill.
- →A big array already in memory: virtualize. There's nothing to fetch.
- →Unbounded server data, shallow browsing: infinite scroll alone.
- →Unbounded data + long sessions or a live feed: infinite scroll + virtualization.
- →Infinite scroll on the API: always cursor pagination, never offset.
- →Combining infinite scroll + virtualization: trigger the fetch off endIndex, not a DOM sentinel.
Read the full article →
The reasoning behind every cell above, plus a build-from-scratch virtualized list in plain React.