Portal Community

Why Virtualization Is Needed

Without virtualization, a log buffer of 10,000 entries would create 10,000 DOM nodes. Rendering and updating 10,000 nodes causes significant scroll lag and React reconciliation cost. With virtualization, the DOM contains at most ~30–50 rows at any time regardless of the buffer size.

How It Works

The Logs tab uses react-window (FixedSizeList) for virtualization. Each row has a fixed height (32px) so the total scrollable height can be computed without rendering all rows:

import { FixedSizeList } from 'react-window';

<FixedSizeList
  height={panelContentHeight}    // available height in the panel
  itemCount={visibleLogs.length} // filtered log count
  itemSize={32}                  // pixels per row
  width="100%"
  overscanCount={5}              // render 5 extra rows above/below viewport
>
  {({ index, style }) => (
    <LogRow
      log={visibleLogs[index]}
      style={style}              // absolute position set by react-window
      searchTerm={searchTerm}
      onExpand={handleExpand}
    />
  )}
</FixedSizeList>

Auto-Scroll to Bottom

While the execution is running, the list auto-scrolls to the bottom as new entries arrive — similar to a terminal. If the user scrolls up to read earlier entries, auto-scroll pauses. It resumes when the user scrolls back to within 50px of the bottom:

const isAtBottom = scrollOffset >= (totalHeight - viewportHeight - 50);

useEffect(() => {
  if (isAtBottom && isRunning) {
    listRef.current?.scrollToItem(visibleLogs.length - 1, 'end');
  }
}, [visibleLogs.length, isAtBottom, isRunning]);

Performance Numbers

MetricValue
Row height32px (fixed)
Rendered DOM rows~30–50 (visible + overscan)
Max buffer size10,000 entries
Auto-scroll resume threshold50px from bottom
Overscan rows5 above + 5 below viewport