VK
Vallarasu K.
Back to All ArticlesFrontend Architecture

Rendering 100,000+ Spreadsheet Cells in React with 60 FPS Virtualization

Overcoming DOM tree bloat and memory leaks when building Universal Spreadsheet Editor with 1,300+ installs.

VKVallarasu Kanthasamy•October 30, 2026•8 min read
#React#DOM Virtualization#Web Performance#JavaScript#Developer Tools

Why Browsers Choke on Large Data Grids

If you attempt to render a standard HTML `<table>` containing 5,000 rows and 20 columns in React, the browser creates **100,000 distinct DOM nodes**.

The consequences are catastrophic:

  • Initial DOM layout takes over 2.5 seconds.
  • Scrolling stutters at 12–18 frames per second.
  • Mobile browsers frequently crash due to JavaScript heap exhaustion.
  • When building the **Universal Spreadsheet Editor** (which recently crossed 1,300+ installs on the Chrome & web ecosystems), my goal was to load files with 100,000+ cells while maintaining a rock-solid **60 FPS scroll performance**.

    The Virtualization Solution: Render Only What Is Visible

    Virtualization (or windowing) solves this by ensuring only the cells currently inside the user's viewport—plus a small buffer—are ever mounted into the DOM.

    If the viewport is 800px tall and each row is 32px high, the browser only needs to mount approximately **28 to 35 rows at any given moment**, regardless of whether the underlying dataset has 50 rows or 500,000 rows.

    typescript
    interface VirtualWindow {
      startIndex: number;
      endIndex: number;
      topOffset: number;
    }
    
    export function computeVisibleRows(
      scrollTop: number,
      viewportHeight: number,
      rowHeight: number,
      totalRows: number,
      overscan = 5
    ): VirtualWindow {
      const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
      const visibleCount = Math.ceil(viewportHeight / rowHeight) + overscan * 2;
      const endIndex = Math.min(totalRows - 1, startIndex + visibleCount);
      const topOffset = startIndex * rowHeight;
    
      return { startIndex, endIndex, topOffset };
    }

    Memory Optimization: Web Workers for CSV Parsing

    Parsing a 40 MB CSV string on the main browser thread freezes the UI for up to 1,500ms. To keep the interface responsive, parsing is offloaded to a **dedicated Web Worker**:

  • Raw file chunks are streamed via the `FileReader` API.
  • The Web Worker tokenizes cells into typed flat arrays.
  • Only sliced view arrays are transferred back to the main UI thread via zero-copy `Transferable Objects`.
  • This dual architecture—Web Worker data processing coupled with DOM virtualization—allows users to navigate, sort, and edit massive spreadsheets right inside their browser with instantaneous responsiveness.

    Available for New Projects

    Need Help Building or Optimizing Your Software?

    I partner with founders and engineering teams on custom MVPs, performance turnarounds, and Chrome extensions with fixed quotes and fast delivery.