Rendering large lists without virtualization is a common performance pitfall that can lead to sluggish UI and poor user experience. Here’s how to properly handle large datasets in React applications.
The Core Problem
Problematic implementation:
function BigList({ items }) { // Could be thousands of items
return (
<ul>
{items.map(item => (
<li key={item.id}>
<ComplexListItem item={item} />
</li>
))}
</ul>
);
}
Issues this causes:
- Slow initial render
- Janky scrolling
- High memory usage
- Poor responsiveness
Solution: Virtualization
1. Using React-Window (Recommended)
import { FixedSizeList as List } from 'react-window';
function BigList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
<ComplexListItem item={items[index]} />
</div>
);
return (
<List
height={600}
width={300}
itemCount={items.length}
itemSize={100} // Height of each row
>
{Row}
</List>
);
}
2. Using React-Virtualized
import { List } from 'react-virtualized';
function BigList({ items }) {
const rowRenderer = ({ key, index, style }) => (
<div key={key} style={style}>
<ComplexListItem item={items[index]} />
</div>
);
return (
<List
width={300}
height={600}
rowCount={items.length}
rowHeight={100}
rowRenderer={rowRenderer}
/>
);
}
Alternative Approaches
1. Pagination
function PaginatedList({ items }) {
const [page, setPage] = useState(1);
const itemsPerPage = 50;
const paginatedItems = items.slice(
(page - 1) * itemsPerPage,
page * itemsPerPage
);
return (
<div>
<ul>
{paginatedItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<Pagination
currentPage={page}
totalItems={items.length}
itemsPerPage={itemsPerPage}
onPageChange={setPage}
/>
</div>
);
}
2. Infinite Loading
import { useInfiniteQuery } from 'react-query';
function InfiniteList() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = useInfiniteQuery(
'items',
({ pageParam = 0 }) => fetchItems(pageParam),
{
getNextPageParam: (lastPage) => lastPage.nextPage,
}
);
return (
<ul>
{data.pages.map(page => (
page.items.map(item => (
<li key={item.id}>{item.name}</li>
))
))}
{hasNextPage && (
<button
onClick={() => fetchNextPage()}
disabled={isFetchingNextPage}
>
{isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
)}
</ul>
);
}
Performance Optimization Techniques
1. Memoize List Items
const MemoizedListItem = React.memo(function ListItem({ item }) {
return <ComplexListItem item={item} />;
});
function BigList({ items }) {
return (
<ul>
{items.map(item => (
<MemoizedListItem key={item.id} item={item} />
))}
</ul>
);
}
2. Windowing with Custom Implementation
function VirtualList({ items, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef();
const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.floor(scrollTop / itemHeight);
const visibleItems = items.slice(startIndex, startIndex + visibleCount);
const handleScroll = () => {
setScrollTop(containerRef.current.scrollTop);
};
return (
<div
ref={containerRef}
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ height: `${items.length * itemHeight}px` }}>
{visibleItems.map((item, index) => (
<div
key={item.id}
style={{
position: 'absolute',
top: `${(startIndex + index) * itemHeight}px`,
height: `${itemHeight}px`
}}
>
<ComplexListItem item={item} />
</div>
))}
</div>
</div>
);
}
Best Practices
- Always use virtualization for lists with more than 100 items
- Set proper item sizes for accurate scroll behavior
- Memoize expensive item components to prevent unnecessary re-renders
- Consider pagination when virtualization isn’t enough
- Use CSS containment for complex list items:
.list-item {
contain: content;
}
- Avoid inline styles/functions in list items that prevent memoization
- Measure performance with React DevTools Profiler
Key Takeaways
- Virtualization libraries (react-window, react-virtualized) solve this problem
- Only render visible items to maintain performance
- Pagination/infinite loading are good alternatives
- Proper key assignment remains crucial
- Memoization helps with complex list items
Proper handling of large lists ensures your React application remains performant and responsive regardless of dataset size. Virtualization should be your default approach when dealing with lists that might contain hundreds or thousands of items.