Hooks

Use React hooks to read editor state, run commands, search documents, and configure pages.

Call these hooks inside <DocxEditor.Root>. You can also call them inside <DocxEditor>, which renders that root. The hooks read the editor from React context.

The packaged toolbar, menu, and navigation pane use the same hooks.

useEditorCommand

Pass a chrome slot ID to useEditorCommand. The chrome slot reference lists every available ID and its named toolbar part. The hook returns the state and action for a control:

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>
  );
}
FieldTypeDescription
execute()() => booleanRuns the command and reports whether it applied.
isActivebooleanReports the command state at the caret.
isEnabledbooleanReports whether the command can run.
disabledReasonstring | nullExplains why the command cannot run.

Use isEnabled as the enabled-state source. Use disabledReason when you explain a disabled control.

Pass an EditorCommand when no chrome slot matches your action:

const suggest = useEditorCommand({ type: 'setEditingMode', mode: 'suggesting' });

useEditorState

Use useEditorState to subscribe to part of the editor snapshot. The hook runs the selector for each state update. It renders your component only when the selected value changes:

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

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

function SaveButton() {
  const dirty = useEditorState((s) => s.canUndo ?? false);
  return <button disabled={!dirty}>Save</button>;
}

Pass a comparison function as the second argument for object values:

const formatting = useEditorState(
  (s) => s.formatting,
  (a, b) => a?.bold === b?.bold && a?.italic === b?.italic
);

Select only the state that your component needs. A page indicator does not need to render after a bold-state change.

Useful fields include:

  • page
  • selection and selectionCollapsed
  • formatting
  • table and image
  • editable
  • isLoading and isOpening
  • parseError
  • editingMode
  • canUndo and canRedo
  • pageSetup
  • fontSubstitutions
  • hasReviewContent
  • lastRejection

useDocxEditor

useDocxEditor returns the editor instance. It returns null before the DocxEditor.Root mount effect creates the instance. It also returns null outside a DocxEditor.Root. Use the instance for actions and one-time reads:

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

function SaveButton() {
  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 your component. Use useEditorState for reactive state.

useEditorEvent

Use useEditorEvent to subscribe for the component's lifetime:

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

useEditorEvent('selectionChange', () => setPanelOpen(false));
useEditorEvent('change', (change) => void autosave(change.revision));

useFontFamily

useFontFamily provides font-picker state. It returns the current value, options, setter, and enabled state:

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

function FontPicker() {
  const font = useFontFamily();

  return (
    <select
      value={font.value ?? ''}
      disabled={!font.isEnabled}
      onChange={(e) => font.setValue(e.target.value)}
    >
      {font.options.map((family) => (
        <option key={family} value={family}>
          {family}
        </option>
      ))}
    </select>
  );
}

useParagraphStyle returns the same shape for paragraph styles.

usePageSetup

Use usePageSetup to read and change the current section. The hook supports margins, orientation, and paper size:

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

function OrientationToggle() {
  const { pageSetup, apply, isEnabled } = usePageSetup();
  const landscape = pageSetup?.orientation === 'landscape';

  return (
    <button
      disabled={!isEnabled}
      onClick={() => apply({ orientation: landscape ? 'portrait' : 'landscape' })}
    >
      {landscape ? 'Portrait' : 'Landscape'}
    </button>
  );
}

useParagraphFormat

Use useParagraphFormat to read and change the paragraph at the selection. apply sends the supplied fields as one command, so one call creates one undo step:

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

function DoubleSpaceButton() {
  const { format, apply, isEnabled } = useParagraphFormat();
  const isDouble = format?.lineSpacing?.value === 2;

  return (
    <button
      disabled={!isEnabled}
      onClick={() => apply({ lineSpacing: { rule: 'multiple', value: isDouble ? 1 : 2 } })}
    >
      {isDouble ? 'Single space' : 'Double space'}
    </button>
  );
}

A field is null when selected paragraphs have different values. A checkbox shows an indeterminate state. A number field has no indeterminate state. It shows a default until you change it. Omitted fields are not written, so existing values stay. Spacing, line-spacing, and indent fields also accept null. That value clears the local setting so the style supplies it. Writing 0 sets an explicit value instead.

For the whole form, DocxEditor.ParagraphDialog is the Paragraph dialog over this hook.

useDocumentOutline

useDocumentOutline returns headings in document order. It also provides an action that moves to a heading:

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

function Outline() {
  const { items, selectedBlockId, goTo, isEmpty } = useDocumentOutline();
  if (isEmpty) return <p>No headings</p>;

  return (
    <ul>
      {items.map(({ heading, depth }) => (
        <li key={heading.blockId} style={{ paddingLeft: depth * 12 }}>
          <button
            data-active={heading.blockId === selectedBlockId || undefined}
            onClick={() => goTo(heading.blockId)}
          >
            {heading.text}
          </button>
        </li>
      ))}
    </ul>
  );
}

Each item has the shape { heading, depth }. heading has the shape { text, level, blockId }. depth measures indentation from the shallowest heading in the document. This calculation aligns a top-level Heading 2 with the base. Use headings when you need the flat list without calculated indentation.

useDocumentSearch

useDocumentSearch provides delayed search and match navigation:

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

function Find() {
  const search = useDocumentSearch();

  return (
    <>
      <input value={search.query} onChange={(event) => search.setQuery(event.target.value)} />
      <span>
        {search.matches.length === 0 ? 0 : search.activeIndex + 1}
        {' / '}
        {search.matches.length}
        {search.truncated && '+'}
      </span>
      <button onClick={search.previous}>Prev</button>
      <button onClick={search.next}>Next</button>
    </>
  );
}

The result also includes these pairs:

  • matchCase and setMatchCase
  • wholeWord and setWholeWord

Other hooks

HookReturn value
useDocxSource(source, options?)Fetches bytes and fonts for a URL, File, or Blob. It supports cancellation.
useEditorValueCommand(slotId)Provides state for value commands such as 'image.wrap' and 'image.altText'.
useParagraphIndent()Provides current indents and an apply action.
useHyperlinkPopup()Provides state for a custom hyperlink panel.
useContentControl()Provides content-control locks, value writes, and form-fill state.
useHeaderFooterState()Returns the active header or footer scope, or null.
useNoteScopeState()Returns the active footnote or endnote scope.
useContextMenuTarget()Returns the element that received the last context-menu action.
useNavigationPane(options?)Provides navigation-pane open state and width.
useTranslation()Returns { t } for the active locale catalog.
useChromeTranslate(overrides?)Returns a catalog resolver that checks an override Map first.
useFonts(source, ...fragments)Builds a stable FontResolver. See the Fonts guide.
useNotePropertiesState()Provides note-numbering properties for the current scope.
useEditorSnapshot(editor)Returns a revision counter for useSyncExternalStore.
useNavigationShift()Returns the horizontal offset for an open navigation pane.
useReviewGutter()Returns the inline reservations for the active review rail.
useTableBorderTargetLabel()Returns the active table-border target label.
useReviewAuthors()Returns tracked-change authors, then comment-only authors. Each item includes its resolved style.
useEditorCaret()Returns { paragraphId, offset } for APIs that accept an at position.
useZoom()Reads and sets zoom from custom chrome.
useToolbarContext()Returns toolbar compound context for custom slot parts.
useToolbarLabel()Returns the active toolbar-scope label.
useToolbarLabelFor(slotId)Resolves the label for one slot ID.
useScopeClassName()Returns the scoped chrome class prefix.
useScopedChromeAnchor()Returns anchor metadata for scoped overlay chrome.

Use useContentControlInstance() outside the owning content-control part. Use useHyperlinkPopupInstance() outside the owning hyperlink part. These hooks provide context-free variants of the corresponding hooks.

@docx-editor.dev/pro/react provides useReview, useReviewOf, useReviewItem, and useReviewAuthor. @docx-editor.dev/pro/vue provides the Vue equivalents.

Next steps

On this page