How would you optimize a React application rendering 100k+ items in a list?
We’ve all been there. You build a sleek dashboard, fetch some data, and map over it to display a list. Everything feels snappy. Then, production data hits. Suddenly, that "simple list" has 100,000 rows, your smooth 60fps scroll turns into a jagged slideshow, and now your browser tab starts consuming memory like it’s going out of style.
If you are dealing with a dataset this large, standard React rendering won't cut it.
Whether you are prepping for a system design interview or fixing a sluggish production app, here is the deep dive on how to handle massive lists in React without crashing the browser.
The Core Problem: It’s Not React, It’s the DOM
Before we fix it, we have to understand why it breaks?
React is fast, but the Document Object Model (DOM) is expensive. If you try to render 100,000 <div> elements at once, the browser has to create 100,000 nodes in memory, calculate their styles, compute their layout (reflow), and paint them to the screen.
Even if the user can only see 10 items on their screen, a standard map function renders all 100,000. This is DOM Bloat, and it is the primary killer of frontend performance.
So, how do we fix it? We cheat.
1. Virtualization (Windowing) – The "Silver Bullet"
The most effective solution for this specific problem is List Virtualization (also called "Windowing").
Instead of rendering all 100k items, you only render the items currently visible in the user's viewport (plus a small buffer). If your screen can fit 10 items, you render ~15 items. As the user scrolls, you recycle those DOM nodes, swapping out the old data for the new data.
To the user, it looks like an infinite list. To the browser, it’s just a tiny, manageable set of elements.
How to implement it
Don't reinvent the wheel here. The community standard libraries are react-window (lighter weight) or react-virtualized (more feature-rich), both written by Brian Vaughn from the React team.
Here is a simple example using react-window:
Why this works:
Memory Efficiency: You have constant memory usage regardless of list size.
Performance: Browsers handle style recalculations for 20 nodes instantly; 100k nodes takes seconds.
2. Pagination and Infinite Scroll
If you don't need all 100k items available on the client side instantly, don't fetch them all.
Pagination: The classic "Page 1, 2, 3" approach. It is predictable and great for SEO, but can feel clunky for data exploration.
Infinite Scroll: As the user reaches the bottom of the list, you fetch the next chunk of data.
Pro Tip: You can combine Infinite Scroll with Virtualization. You fetch data in chunks (lazily) so you don't kill the network, and you virtualize the rendering so you don't kill the DOM.
3. Strict Memoization (React.memo)
Virtualization handles the container, but what about the items inside?
If you have a list where items update frequently (e.g., a live stock ticker or chat log), you need to ensure that changing Item #5 doesn't cause Item #99 to re-render.
Wrap your list items in React.memo. This tells React to only re-render the component if its props have actually changed.
Warning: Don't pass inline functions (like onClick={() => handleDelete(id)}) to these memoized components, or you will break the memoization. Use useCallback for your event handlers.
4. Web Workers (Offloading the Heavy Lifting)
Sometimes the rendering isn't the only bottleneck - processing the data is.
If you need to filter, sort, or transform those 100k items before showing them, doing that on the main JavaScript thread will freeze the UI. The main thread handles user interactions (clicking, scrolling); if it's busy sorting an array, the app feels "frozen."
Move heavy data logic to a Web Worker. This runs the script in a background thread.
Main Thread: Sends raw data to Worker.
Worker: Sorts/Filters the 100k items.
Worker: Sends processed data back to Main Thread.
Main Thread: Updates state and React renders (virtualized, of course).
5. The CSS Trick: content-visibility
If you cannot use virtualization (perhaps due to complex layout requirements like a masonry grid with dynamic heights), modern CSS offers a fallback.
The content-visibility: auto property tells the browser: "Hey, skip the rendering work for this element until it actually enters the viewport."
It is not as performant as react-window, but it is basically "lazy loading" for rendering, implemented entirely in CSS. Note that browser support is improving but not universal yet.
Summary Checklist
If you are staring down a 100k item requirement, follow this hierarchy:
Virtualization: Use
react-window. This is non-negotiable for lists this size.Lazy Loading: Don't fetch 100k items at once unless you have to.
Memoization: Prevent unnecessary re-renders of individual rows.
Offloading: Use Web Workers if you are doing heavy math on the data.
Handling large data in the frontend is less about raw power and more about being smart with resources. Render strictly what you need, when you need it.

