Zoom and fit to screen

Fit the document to the viewport, control the zoom lifecycle from your own UI with the useZoom hook, and let the comments pane shrink the page instead of pushing it off screen.

The editor fits the page to its container by default. A window with room for the sheet renders at 100%; a narrower one shrinks the document instead of growing a horizontal scrollbar.

The two modes

Zoom has a value and a mode. The value is the scale the pages paint at; the mode is where that value comes from.

ModeBehavior
'auto' (default)Fit the page width, between 50% and 100%. Shrinks a page that does not fit, leaves one that does.
{ type: 'fit', fit: 'pageWidth' }Fit the page width in both directions, so a wide window magnifies the page.
{ type: 'fixed' }Hold whatever zoom is set to. This is the pre-fit behavior.
<DocxEditor document={bytes} />                          {/* auto */}
<DocxEditor document={bytes} zoomMode={{ type: 'fit', fit: 'pageWidth' }} />
<DocxEditor document={bytes} zoom={1} zoomMode={{ type: 'fixed' }} />

Passing zoom on its own also means fixed, so an app that pinned a scale keeps it.

A fit tracks the container. Resizing the window, opening the comments pane, docking the navigation pane: anything that changes the room beside the page changes the scale, in the engine, with nothing to wire up.

Setting a level ends the fit. That is what you want from a zoom control: a reader who picks 150% has said so, and a resize must not take it back.

Driving it yourself

useZoom is the whole lifecycle in one hook.

import { useZoom } from '@docx-editor.dev/react';

function ZoomControl() {
  const { zoom, isFit, auto, zoomIn, zoomOut, canZoomIn, canZoomOut } = useZoom();

  return (
    <div>
      <button onClick={zoomOut} disabled={!canZoomOut}>
        &minus;
      </button>
      <span>{Math.round(zoom * 100)}%</span>
      <button onClick={zoomIn} disabled={!canZoomIn}>
        +
      </button>
      <button onClick={auto} aria-pressed={isFit}>
        Fit
      </button>
    </div>
  );
}
MemberWhat it does
zoomThe resolved scale. 1 is 100%.
mode, isFitWhere the scale came from. Render a control's selected state from these, not from zoom.
setZoom(n)A fixed scale. Leaves any fit.
setMode(m)'auto', a fit, or { type: 'fixed' }.
auto(), fitToWidth()The two fits, by name.
reset()Back to a plain, untracked 100%.
zoomIn(), zoomOut()Walk the preset ladder in levels.
canZoomIn, canZoomOutWhether there is a rung left in that direction.

Render the selected state from mode, not from zoom. A menu that ticks the level matching the percentage will light up "75%" while the editor is tracking the viewport and about to move off it.

The packaged toolbar's zoom control already does all of this: Automatic and Fit width sit above the preset levels, and the tick follows the mode rather than the percentage.

It ticks those two modes and the preset levels — the things it can put a reader into. A fit with bounds of your own is not one of them, so the menu shows the resolved percentage with nothing ticked, rather than lighting up a row that would silently replace your bounds when clicked.

Comments, and narrow screens

The comments rail reserves a gutter beside the page. Under a fit that gutter comes out of the document's width, so opening comments shrinks the page rather than pushing it off screen: the two sit side by side and both stay readable.

There is a width below which that stops helping. The rail takes a fixed 316px whether or not the container can spare it, so on a phone with comments open the page would be fitted into a sliver — a document nobody can read, to avoid a scrollbar nobody minds. So 'auto' has a floor of 50%: below that the page keeps a legible size and the container scrolls sideways, which is the ordinary answer to "this does not fit".

Set your own floor if 50% is not where you want it:

<DocxEditor document={bytes} zoomMode={{ type: 'fit', fit: 'pageWidth', minZoom: 0.35 }} />

Without React

Zoom lives in the engine, so every host reaches it the same way:

editor.setZoomMode('auto');
editor.getZoom(); // 0.79 on a container too narrow for the page
editor.getZoomMode(); // { type: 'fit', fit: 'pageWidth', minZoom: 0.5, maxZoom: 1 }

editor.setZoom(1.5); // leaves the fit
editor.getZoomMode(); // { type: 'fixed' }

snapshot() carries zoom and zoomMode, and both fire on selectionChange — including when the editor refits itself after a resize, so a subscriber never shows a stale percentage.

On this page