Core reference / Virtualization

Virtualization

Virtualization is always enabled in OmniGrid — no opt-in is required. The core engine renders only the rows and columns currently visible in the scroll window, plus a small overscan margin. Off-screen nodes are recycled and repositioned via transform: translateY, which keeps the DOM node count constant regardless of dataset size.

01

How it works

The virtualizer maintains a pool of DOM row nodes. On scroll it:

  1. Recomputes the viewport range (start row index, end row index, visible columns).
  2. Reuses pooled nodes, updating each node's transform to the correct offset and binding new row data.
  3. Leaves the DOM structure unchanged — no creation or destruction of nodes on the scroll hot path.

Because row height is fixed (see rowHeight in Grid options), the virtualizer can compute exact offsets without measuring individual rows — the fundamental assumption that makes window rendering O(1) per scroll frame rather than O(n).

02

Scroll decoupling

Scroll-only changes (scrollTop / scrollLeft) flow through an ephemeral path that does not notify the React store. This means scrolling never triggers a React re-render — the DOM pool handles it imperatively. React re-renders are reserved for structural changes only: data mutations, column updates, and dimension changes.

03

Pinned columns

Horizontally pinned columns (left / right) back onto their own DOM pool. Each pin pane is a sibling inside the single scroll container and uses position: sticky so it shares the container's native vertical scroll — no imperative scrollTop sync required. On the horizontal axis, the pane's offset is translated into local coordinates so pinned cells always stay in view.

04

Tuning

Two options let you widen the rendered window to reduce edge artefacts:

OptionDefaultDescription
rowOverscan4Extra rows rendered above and below the visible viewport to prevent blank flashes during fast scroll.
columnOverscan0Extra columns rendered beyond the horizontal scroll window.
05

Usage

const gridOptions = {
  columns: colDefs,
  data: rows,
  getRowId: (row) => row.id,
  // Overscan rows render extra content above and below the
  // visible window to prevent blank flashes on fast scroll.
  rowOverscan: 10,
  columnOverscan: 2,
};