Large Data Table Performance
Architectural strategies for rendering thousands of rows in a data table without freezing the browser or exhausting memory.
The Problem
When rendering a table with thousands of rows (e.g., an analytics dashboard or user directory), the browser must create tens of thousands of DOM nodes. This completely freezes the main thread, delays interactivity, and consumes massive amounts of RAM.
Common Symptoms
- The page completely freezes for 1-5 seconds when navigating to the table.
- Scrolling feels extremely sluggish and unresponsive.
- Hover effects and tooltips are severely delayed.
- Lighthouse reports "Avoid an excessive DOM size".
Root Causes
- Creating DOM nodes is computationally expensive.
- Each DOM node requires memory for styles, event listeners, and layout boxes.
- React/Vue must diff massive component trees during updates, which is an O(N) operation where N is the number of nodes.
Typical Expected Improvements
Decision Matrix
Architectural decision guidance for different scenarios.
Scenario: Table has 5,000+ rows and users need to scroll quickly through them.
⚖️ Trade-offs: Breaks native browser "Find in Page" (Ctrl+F) because unrendered rows do not exist in the DOM.
💡 Why: Virtualization only renders the rows visible in the viewport plus a small buffer. It provides a seamless scrolling experience while keeping DOM count strictly bounded.
Scenario: Users need to browse large datasets but also rely heavily on "Find in Page".
⚖️ Trade-offs: UX friction; users have to explicitly click to see more data. However, native browser search works perfectly for the current page.
💡 Why: Pagination restricts the DOM size naturally and avoids the complexity of scrolling math.
- If the table always displays less than 100 rows.
- If the table rows have highly unpredictable heights that change dynamically based on network requests, making virtualization mathematically complex.
- If SEO requires all rows to be present in the HTML payload for crawlers (use server-side pagination instead).
Approaches
Do This
- Use a Virtualization library (`react-window`, `vue-virtual-scroller`).
- Delegate heavy sorting and filtering to Web Workers.
- Use CSS `content-visibility: auto` for off-screen rows (if virtualization is not possible).
Anti-patterns
- Rendering thousands of rows using a raw `.map()` or `v-for`.
- Attaching individual event listeners to every cell. Use event delegation on the `<tbody>`.
- Triggering state updates in the parent component that force the entire table to re-render unnecessarily.
Implementation
import { useVirtualList } from '@vueuse/core' const { list, containerProps, wrapperProps } = useVirtualList( hugeDataArray, { itemHeight: 48 } )
Use AI
Instantly analyze your codebase using this recipe via MCP. Open your AI agent and run this prompt.