⚡ Performance.now() = Before: 8.5s | After: 0.8s
PROBLEM: Rendering 10,000 items BOTTLENECK: DOM manipulation SOLUTION: Virtual scrolling / windowing RESULT: 10x faster, 90% less memory
The Problem: You’re Rendering What Users Can’t See
❌ The Slow Way
// Render ALL 10,000 items
const list = document.getElementById('list');
items.forEach(item => {
const div = document.createElement('div');
div.textContent = item.name;
list.appendChild(div);
});
// Problem:
// - 10,000 DOM nodes created
// - 10,000 reflows
// - Only 20 visible on screen
// - Browser rendering 9,980 invisible items
// = 8.5 seconds to render
// = 500MB memory usage
// = Scroll jank
✅ Virtual Scrolling (react-window)
import { FixedSizeList } from 'react-window';
function MyList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={600} // Viewport height
itemCount={10000} // Total items
itemSize={50} // Each item height
width="100%"
>
{Row}
</FixedSizeList>
);
}
// Result:
// - Only renders ~20 visible items
// - Recycles DOM nodes as you scroll
// - Total DOM nodes: ~30 (includes buffer)
// = 0.8 seconds initial render
// = 50MB memory (90% less!)
// = Buttery smooth scrolling
| Metric | Normal Rendering | Virtual Scrolling | Improvement |
|---|---|---|---|
| Initial Render | 8.5s | 0.8s | 10.6x faster |
| Memory Usage | 500 MB | 50 MB | 90% less |
| DOM Nodes | 10,000 | ~30 | 99.7% less |
| Scroll FPS | 15 fps (janky) | 60 fps (smooth) | 4x smoother |
📚 Virtual Scrolling Libraries
react-window
Best for: Simple lists, grids
Bundle size: 6.5KB
API: Simple, easy to learn
Docs: ⭐⭐⭐⭐⭐
react-virtualized
Best for: Complex features
Bundle size: 30KB (heavier)
API: More features, steeper curve
Docs: ⭐⭐⭐⭐
@tanstack/virtual
Best for: Framework agnostic
Bundle size: 5KB
API: Modern, flexible
Docs: ⭐⭐⭐⭐⭐
Vanilla JS
Best for: No framework
Bundle size: You write it
API: Full control
Docs: DIY
🎯 When to Use Virtual Scrolling
- Lists with 100+ items → Definitely
- Lists with 50-100 items → Probably
- Lists with <50 items → Overkill, don’t bother
- Infinite scroll feeds → Absolutely
- Data tables → Yes, with react-window or tanstack-virtual
- Image galleries → Yes, prevents memory leaks
“Our admin dashboard showed 10,000 user records. It took 12 seconds to load and crashed mobile browsers. After implementing react-window, load time dropped to 0.9s and it runs perfectly on phones.”
💡 Other Performance Wins
- Debounce search inputs (lodash.debounce, 300ms delay)
- Lazy load images (loading=”lazy” attribute)
- Code splitting (React.lazy, dynamic imports)
- Memoization (useMemo, React.memo for expensive components)
- Web Workers (heavy computation off main thread)
