Virtualization for Large Lists: Rendering 10,000+ Annotations Without Lag
Key Takeaway
Our annotation list rendered all 10,000+ items in the DOM simultaneously, causing severe performance issues. Implementing react-window for virtualization reduced DOM nodes from 10,000 to ~20 and improved scroll performance from 5 FPS to 60 FPS.
The Problem
function AnnotationList({ annotations }: { annotations: Annotation[] }) {
// Renders ALL 10,000 items! 🔥
return (
<div>
{annotations.map(annotation => (
<AnnotationItem key={annotation.id} annotation={annotation} />
))}
</div>
);
}
Issues: 5 FPS scrolling, 15-second initial render, 45MB DOM size, browser freezes, mobile crashes.
The Solution
import { FixedSizeList } from 'react-window';
function AnnotationList({ annotations }: { annotations: Annotation[] }) {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>
<AnnotationItem annotation={annotations[index]} />
</div>
);
return (
<FixedSizeList
height={600} // Viewport height
itemCount={annotations.length} // 10,000
itemSize={80} // Row height
width="100%"
>
{Row}
</FixedSizeList>
);
}
// Only renders ~20 visible items!
Variable-size items:
import { VariableSizeList } from 'react-window';
function VariableAnnotationList({ annotations }: Props) {
const listRef = useRef<VariableSizeList>(null);
const getItemSize = (index: number) => {
const annotation = annotations[index];
return annotation.type === 'detailed' ? 120 : 60;
};
return (
<VariableSizeList
ref={listRef}
height={600}
itemCount={annotations.length}
itemSize={getItemSize}
width="100%"
>
{Row}
</VariableSizeList>
);
}
Impact
| Metric | Before | After | |--------|--------|-------| | DOM nodes | 10,000 | 20 | | Initial render | 15s | 0.2s | | Scroll FPS | 5 | 60 | | Memory usage | 450MB | 45MB |
Lessons Learned
- Virtualize Long Lists: Only render visible items
- react-window is Lightweight: Better than react-virtualized for most cases
- Fixed Size is Fastest: Use when possible
- Cache Item Sizes: For variable-size lists
- Windowing Threshold: Virtualize at 100+ items