Large lists (say 10k+ rows) can easily kill React performance if rendered naively.
Let’s break it down step by step:
πΉ Problem with large lists
- Rendering all items at once → causes:
- Slow initial render
- High memory usage
- Laggy scrolling
- Example:
function BadList({ items }) {
return (
<ul>
{items.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
);
}
If items has 10,000 entries → browser chokes β
πΉ Efficient ways to handle large lists in React
1. Windowing / Virtualization β (Most common)
- Render only visible items instead of all items.
- Example libraries:
react-window(lightweight)react-virtualized(feature-rich)
β
Example with react-window:
import React from "react";
import { FixedSizeList as List } from "react-window";
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);
function VirtualizedList() {
return (
<List
height={400} // visible height
itemCount={items.length}
itemSize={35} // row height
width={300}
>
{({ index, style }) => (
<div style={style}>
{items[index]}
</div>
)}
</List>
);
}
export default VirtualizedList;
π Only ~10–15 items render at a time; rest are recycled as you scroll. π
2. Pagination / Infinite Scrolling
- Instead of loading/rendering all items → load in chunks.
- Pagination → page numbers (classic).
- Infinite Scroll → load more when scrolling near the end.
β Example (Infinite Scroll):
import React, { useState, useEffect } from "react";
function InfiniteScrollList() {
const [items, setItems] = useState(Array.from({ length: 20 }, (_, i) => `Item ${i}`));
const loadMore = () => {
setItems(prev =>
[...prev, ...Array.from({ length: 20 }, (_, i) => `Item ${prev.length + i}`)]
);
};
useEffect(() => {
const handleScroll = () => {
if (window.innerHeight + window.scrollY >= document.body.scrollHeight - 100) {
loadMore();
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<ul>
{items.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
);
}
export default InfiniteScrollList;
3. Memoization & Pure Components
- Prevent unnecessary re-renders of list items.
- Use
React.memoon list item component.
β Example:
const ListItem = React.memo(({ item }) => {
console.log("Rendered:", item);
return <li>{item}</li>;
});
function MemoizedList({ items }) {
return (
<ul>
{items.map((item, i) => <ListItem key={i} item={item} />)}
</ul>
);
}
π Now only changed items re-render, not all.
4. Chunk Rendering (Time-slicing)
- Render list in small chunks over time instead of blocking UI.
- Example with
setTimeout:
import React, { useState, useEffect } from "react";
function ChunkedList({ items }) {
const [visible, setVisible] = useState([]);
useEffect(() => {
let i = 0;
function showChunk() {
setVisible(prev => [...prev, ...items.slice(i, i + 50)]);
i += 50;
if (i < items.length) {
setTimeout(showChunk, 50); // render next chunk
}
}
showChunk();
}, [items]);
return <ul>{visible.map((item, i) => <li key={i}>{item}</li>)}</ul>;
}
π UI stays responsive.
5. Avoid inline functions/objects
- Inline callbacks or styles in large lists can cause re-renders.
- Extract functions outside map.
πΉ Which approach to use?
| Technique | When to use |
|---|---|
| Virtualization | Very large lists (1k+) → β Best performance |
| Pagination | API-driven data, user expects pages |
| Infinite Scroll | Social feeds, chat apps |
| Memoization | When list items have props that don’t change |
| Chunk Rendering | When virtualization isn’t possible (complex UIs) |
πΉ Real MERN Example
- Suppose you’re building a chat app.
- Backend (
MongoDB + Express) sends messages in pages (limit=50). - Frontend (
React) uses react-window for fast rendering. - As user scrolls → fetch more from Node API → append to list.
- Backend (