Virtualized Rendering

PreviousNext

virtualized is an opt-in DOM strategy for pathological documents. It is experimental and not production-ready. Use it to measure and harden huge-document behavior without making ordinary editors give up native DOM coverage.

When To Use It

Use this mode only when a document is large enough that staged rendering and partial-DOM preview rendering still leave too much DOM or heap pressure.

StrategyUse it forProduction posture
autobounded large-document renderingproduction default
stagedeventual native DOM coverage for the whole documentexplicit product tradeoff
fulldebugging full DOM behaviordebug path
virtualizedviewport-only mounting for pathological documentsexperimental, not production-ready

This mode uses TanStack Virtual internally as the viewport range and measurement engine. Slate still owns selection, DOM coverage boundaries, model copy, and materialization policy.

For paginated editors, prefer page-level virtualization from @platejs/slate-layout. Page virtualization keeps page chrome, layout fragments, and selection projection under one mount boundary.

Usage

Virtualized rendering needs a bounded editable scroll surface. If Slate cannot prove that the editable root is a safe scroll owner, it falls back to staged rendering and reports the actual strategy through metrics.

auto uses bounded partial-DOM rendering for large documents. It does not background-mount the whole document.

<Editable
  domStrategy={{
    estimatedBlockSize: 32,
    overscan: 4,
    type: "virtualized",
    threshold: 25_000,
  }}
  style={{ height: 480, overflowY: "auto" }}
/>
<Editable
  domStrategy={{
    estimatedBlockSize: 32,
    overscan: 4,
    type: "virtualized",
    threshold: 25_000,
  }}
  style={{ height: 480, overflowY: "auto" }}
/>

The public API uses editor-shaped options. It does not expose TanStack's getScrollElement, measureElement, rangeExtractor, item-key hooks, or raw virtualizer options.

Native Behavior Limits

Unmounted content has no native DOM. That is the point of virtualization, and it is also the reason this mode stays experimental.

AreaCurrent behavior
Caret entrySlate materializes the target block before editing.
Broad selectionSlate keeps large selections in the model instead of expanding every block.
CopySlate uses copyPolicy="model" for ranges crossing unmounted content.
Browser findNative find only sees mounted blocks.
Screen readersScreen readers only traverse mounted blocks.
IME and mobile selectionStill outside the production claim until raw-device proof exists.

Do not use this mode when the product requires native find, full screen-reader traversal, or production-grade mobile selection over the full document.

Metrics

Use onDOMStrategyMetrics whenever you test this mode. The callback tells you whether Slate actually used the requested strategy and how much DOM remains mounted.

<Editable
  domStrategy={{ type: "virtualized" }}
  onDOMStrategyMetrics={(metrics) => {
    navigator.sendBeacon(
      "/rum/slate-dom-strategy",
      JSON.stringify({
        degradationMode: metrics.degradationMode,
        documentSize: metrics.documentSize,
        domNodeCount: metrics.domNodeCount,
        effectiveStrategy: metrics.effectiveStrategy,
        mountedTopLevelCount: metrics.mountedTopLevelCount,
        pendingTopLevelCount: metrics.pendingTopLevelCount,
        requestedStrategy: metrics.requestedStrategy,
        viewportVirtualizationBoundaryCount:
          metrics.viewportVirtualizationBoundaryCount,
      })
    );
  }}
  style={{ height: 480, overflowY: "auto" }}
/>
<Editable
  domStrategy={{ type: "virtualized" }}
  onDOMStrategyMetrics={(metrics) => {
    navigator.sendBeacon(
      "/rum/slate-dom-strategy",
      JSON.stringify({
        degradationMode: metrics.degradationMode,
        documentSize: metrics.documentSize,
        domNodeCount: metrics.domNodeCount,
        effectiveStrategy: metrics.effectiveStrategy,
        mountedTopLevelCount: metrics.mountedTopLevelCount,
        pendingTopLevelCount: metrics.pendingTopLevelCount,
        requestedStrategy: metrics.requestedStrategy,
        viewportVirtualizationBoundaryCount:
          metrics.viewportVirtualizationBoundaryCount,
      })
    );
  }}
  style={{ height: 480, overflowY: "auto" }}
/>

If effectiveStrategy is not virtualized, Slate is telling you that the virtualized path did not activate.

Production Gate

Keep this mode behind explicit product flags until these rows are green:

  • caret target materializes before every edit path;
  • copy and paste across unmounted ranges stay model-owned and correct;
  • IME composition does not lose text near virtualized boundaries;
  • mobile selection handles behave deterministically near mounted and unmounted edges;
  • browser find behavior is either custom-owned or explicitly limited;
  • screen-reader behavior has an accepted product strategy;
  • 25k and 50k document stress rows meet edit-latency budgets, not just ready time and DOM-count budgets.

See DOM Coverage Boundaries for the selection, copy, find, and materialization policies used at unmounted ranges.