Hooks

Connect React controls to DOCX editor state and commands with hooks.

Call these hooks from components inside <DocxEditor.Root>. The packaged <DocxEditor> provides that root for its children.

Connect a command button

This button runs a toolbar command and shows why the command cannot run:

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

function BoldButton() {
  const bold = useEditorCommand('text.bold');
  return (
    <button
      onMouseDown={(e) => e.preventDefault()}
      onClick={() => bold.execute()}
      disabled={!bold.isEnabled}
      data-active={bold.isActive || undefined}
      title={bold.disabledReason ?? 'Bold'}
    >
      B
    </button>
  );
}

useEditorCommand(target: ChromeSlotId | EditorCommand): EditorCommandState takes a slot ID from the chrome slot reference, or a raw EditorCommand such as { type: 'setEditingMode', mode: 'suggesting' }.

MemberTypeDescription
execute(options?)booleanRuns the command. Returns true when it applied.
isActivebooleanCommand state at the caret.
isEnabledbooleanWhether the command can run.
disabledReasonstring | nullWhy the command cannot run.
valuestring | nullCurrent value for value slots.

Read state and events

useEditorState subscribes to one slice of the snapshot. The component renders only when that slice changes:

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

function PageIndicator() {
  const page = useEditorState((s) => s.page);
  return (
    <span>
      {page.current} / {page.total}
    </span>
  );
}

Pass a comparator for an object slice. Common fields include page, selection, formatting, editable, isLoading, editingMode, canUndo, pageSetup, and lastRejection.

HookReturnsNotes
useEditorState(selector, isEqual?, options?)TSelected snapshot value.
useDocxEditor()DocxEditorInstance | nullnull before creation and outside a root.
useEditorEvent(event, handler)voidSubscribes for the component lifetime.
useEditorSnapshot(editor)numberRevision counter for useSyncExternalStore.
useEditorCaret(){ paragraphId, offset } | nullCaret for at parameters.
useEditorHostDocument()Document | nullDocument that holds the pages, also in a pop-out window. null on the server.
useEditorColorMode(){ colorMode }'light' or 'dark'. 'light' outside a root.

selectionChange also fires when other state moves without a document change, such as the page count. Compare snapshot.selection with the previous value before you react.

useDocxEditor

Use the instance for actions and one-time reads:

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

function SaveButton({ upload }: { upload: (bytes: ArrayBuffer) => Promise<void> }) {
  const editor = useDocxEditor();
  return (
    <button
      disabled={!editor}
      onClick={async () => {
        const bytes = await editor?.save();
        if (bytes) void upload(bytes);
      }}
    >
      Save
    </button>
  );
}

Calling editor.snapshot() during render does not subscribe. For scrollToAnchor and highlightAnchor, see Navigation.

Choose a hook

Formatting and page setup

HookReturns
useFontFamily(){ value, options, setValue, isEnabled }
useParagraphStyle(){ value, options, setValue, isEnabled } for paragraph styles
useParagraphFormat(){ format, apply, isEnabled }
useParagraphIndent(){ indent, apply, isEnabled }
usePageSetup(){ pageSetup, apply, isEnabled }
useEditorValueCommand(slotId){ value, options, execute, isEnabled, disabledReason }. See Apply a value.
useZoom(){ zoom, mode, levels, isFit, canZoomIn, canZoomOut, setZoom, setMode, zoomIn, zoomOut, reset, fitToWidth, auto }

apply sends one command and returns true when it applied.

apply inputResult
Omitted fieldStays unchanged.
null on spacing or indentClears the local value.
0Sets an explicit zero.

In useParagraphFormat().format, a field is null when the selected paragraphs differ. usePageSetup uses twips for sizes and margins, and scope takes 'section' or 'document'.

useZoom

For a complete zoom control, see Zoom.

Document content

HookReturns
useWatermark(){ watermark, apply, read, isEnabled }
useBookmarks(){ bookmarks, insert, remove, goTo, keepFirst, isWritable }
useGoToBookmark(options?){ query, setQuery, rows, bookmarks, activeIndex, setActiveIndex, move, go, problem, problemName, sortBy, setSortBy, showHidden, setShowHidden }
useCommentDraftRequest(handler)void

useWatermark

apply sends one setWatermark command, so each call is one undo step. Pass null to remove the watermark. read() returns the current watermark as a setWatermark value, including picture bytes.

useBookmarks

Each action sends one command and returns ExecResult. goTo works in a read-only document; isWritable reports whether edits are accepted.

Each name has one entry; duplicates counts copies, and keepFirst(name) removes them without moving the selection.

useGoToBookmark({ enabled }) reads bookmarks only while your UI is open. For details, see Bookmarks.

go(name?) targets name, then the highlighted row. For a nonempty query, it then tries an exact name match, ignoring case, then the first result. It returns ExecResult.

useCommentDraftRequest

Register this handler for your own comment UI. review.addComment stays disabled until a handler is registered, and the latest registration wins. Pass null while your UI cannot open a draft.

The handler takes no arguments. Read the selection with editor.getSelectionPlacement(), and pin it with editor.retainSelection() while the draft is open.

HookReturns
useDocumentOutline(){ items, headings, selectedBlockId, goTo, isEmpty }
useDocumentSearch(options?){ query, setQuery, find, matches, activeIndex, activeMatch, next, previous, goTo, clear, isPending, truncated, matchCase, setMatchCase, wholeWord, setWholeWord }
useHighlights(name, source, options?){ applied, unavailable }
useHighlightAt(name?)Highlight under the pointer, or null
useNavigationPane(options?){ open, setOpen, toggle, tab, setTab, paneWidth, shift, overlay }
useNavigationShift()number, the page offset for an open pane

Each outline item is { heading, depth }, and depth counts from the shallowest heading.

setQuery waits 150 milliseconds; find searches at once. truncated means the search stopped at 2,000 matches. For examples, see Navigation.

useHighlights

A function source reruns after document changes, so wrap it in useCallback. For options, see Highlight text ranges.

Loading, fonts, and locale

HookReturns
useDocxSource(source, options?){ document, fonts, error, isLoading }
useFonts(source, ...fragments) or useFonts(...origins)MarkedFontResolver, stable across renders
useOpenFailure(options?){ failure, message, dismiss }
useTranslation(){ t, catalogue, lang }
useChromeTranslate(overrides?)ChromeTranslate that checks the override Map first
useChromeDir()'ltr' | 'rtl' | undefined

useDocxSource

useDocxSource fetches the document and its fonts. It accepts a URL string, URL, Uint8Array, or ArrayBuffer, and cancels on change.

For font origins, see Fonts. For refused opens, see Open failures.

Review

HookReturns
useReviewAuthors()readonly ReviewAuthorInfo[], tracked-change authors first
useReviewGutter(){ inlineStart, inlineEnd } reserved by the review rail

@docx-editor.dev/pro/react adds useReview, useReviewOf, useReviewItem, useReviewAuthor, useStackedReviewPositions, and useReviewRailMetrics. See Tracked changes.

Popovers, scopes, and toolbar helpers

HookReturns
useHyperlinkPopup(){ state, open, openAtCaret, close, beginEdit, commitEdit, setUrl, setText, copy, unlink, openTarget }
useHyperlinkPopupInstance(active?)Same, outside the owning part
useContentControl(){ control, controls, setValue, remove, formFill, ... }
useContentControlInstance()Same, outside the owning part
useContentControlWidget()Value popup draft, calendar, and actions. selectDay(iso) and selectToday() apply the date at once. See Compose the value popup.
useHeaderFooterState()Active header or footer scope, or null
useNoteScopeState()Active footnote or endnote scope
useNotePropertiesState()Note numbering properties, or null
useContextMenuTarget()HTMLElement | null that received the last context-menu action
useTableBorderTargetLabel()string label of the table-border target
useToolbarContext(){ t, onSave }
useToolbarLabel()(key) => string for the active scope
useToolbarLabelFor(t)(key) => string for a given translator
useScopeClassName()'', 'docx-editor ', or 'docx-editor dark '
useScopedChromeAnchor(findAnchor, placement){ ref, style } for overlay chrome

Dialog draft contexts

Call these hooks in a child of the matching dialog. Each returns values, setValue(name, value), errors, isEnabled, apply(), and cancel():

HookDraft typeDraft keys
usePageSetupDialog()PageSetupDialogFieldspageWidth, pageHeight, orientation, marginTop, marginBottom, marginLeft, marginRight, scope
useParagraphDialog()UseParagraphDialogReturn['values']direction, alignment, indentLeft, indentRight, special, specialBy, spaceBefore, spaceAfter, lineRule, lineValue, contextualSpacing, keepNext, keepLines, widowControl, pageBreakBefore, tabStops, clearedAllTabStops
useTextFormFieldDialog()TextFormFieldDialogFieldsdefaultText, type, maxLength, format, enabled
useRevisionMarkupDialog()ResolvedRevisionMarkupinsertions, deletions, changedLines, movedFrom, movedTo, cells, formatting, trackMoves, trackFormatting
useCompareDocumentsDialog()CompareDocumentsDialogValuesrevised, authorName, detectFormatChanges, removeDateAndTime
useWatermarkDialog()WatermarkDialogFieldskind, imageData, imageName, scale, washout, text, fontFamily, fontSizePt, bold, italic, transparency, color, layout
useBookmarkDialog()BookmarkDialogFieldsname, sortBy, showHidden
useSplitCellDialog()SplitCellDialogFieldscolumns, rows (strings)

Most custom Field names match draft keys, with two exceptions:

DialogField nameDraft keys
Page SetuppageSizeUpdates pageWidth and pageHeight. Neither is a Field name.
ParagraphNoneclearedAllTabStops is draft state only.
HookExtra member
useParagraphDialog()mixed
useRevisionMarkupDialog()reset() restores default draft values. Call apply() to apply them.
useSplitCellDialog()limits: the selected cell's maximum row and column counts, or null

Page sizes, margins, and indents use twips (1,440 per inch). Paragraph spacing uses points, except lineValue with lineRule: 'multiple', which is a line multiple. For the compare dialog's progress and cancel state, see Compare documents.

For a custom input, see Connect a custom input.

Apply a value

useEditorValueCommand reads the selected value and applies a new one. This slider sets the font size:

import { useState } from 'react';
import { useEditorValueCommand, useHistoryGroup } from '@docx-editor.dev/react';

export function FontSizeSlider() {
  const size = useEditorValueCommand('font.size');
  const gesture = useHistoryGroup({ kind: 'range' });
  const [error, setError] = useState<string | null>(null);

  return (
    <label title={size.disabledReason ?? undefined}>
      {size.value === null ? 'Mixed' : `${size.value / 2} pt`}
      <input
        ref={gesture.ref}
        type="range"
        min={16}
        max={144}
        value={size.value ?? 22}
        disabled={!size.isEnabled}
        onChange={(event) => {
          const result = size.execute(Number(event.currentTarget.value), gesture.options());
          setError(result.ok ? null : result.reason);
        }}
      />
      <output aria-live="polite">{error}</output>
    </label>
  );
}

Font size uses half-points: 24 means 12 pt. null means mixed or unavailable, and value follows undo and redo. Other value slots include text.color (six hex digits without #), font.family, text.highlight, styles.style, list.lineSpacing, and image slots.

execute(value, options?) returns ExecResult:

FieldMeaning
okfalse when the editor refused the command.
code, reasonPresent when ok is false. Use code in logic and show reason.
changedtrue when the document changed. Formatting at a caret can change only the typing format.
history?.kindstarted, extended, split, or none for grouped writes. split does not mean failure.

useEditorCommand().execute(options) takes the same options but returns a boolean. For the full result of a raw command, call editor.exec(command, options).

Group an input gesture

useHistoryGroup({ kind }): { ref, options } makes a continuous gesture one undo step. Attach ref to the control, and pass options() to each command from that gesture:

kindUse it forWhat ends the gesture
range, repeatA slider, or a button that repeats while heldPointer release or cancel, lost capture, keyup, or blur
keyboardRepeated keydown eventsKeyup, Escape, or blur
native-colorAn HTML color inputNative change, next activation, Escape, or blur

The binding disposes the group on unmount or editor replacement. For repeat, your app owns the timer. A native color change does not guarantee that the picker closed.

useEditorEvent('historyDiagnostic', handler) reports interrupted groups. To group a review decision, see Group a review decision.

Limits

  • Call hooks inside DocxEditor.Root. Outside it, useDocxEditor() returns null.
  • canUndo reports undo availability, not unsaved changes. Track save completion yourself.
  • selectionChange also fires for state unrelated to the selection.
  • Dialog draft hooks read a draft; they do not open the dialog.
  • Image value commands reject the historyGroup option.
  • useContentControlWidget().showMonth(year, month) accepts integer years 100 through 9999 and zero-based months 0 through 11.

See also