Toolbar

Replace toolbar controls, add responsive groups, and build custom editor interfaces in React and Vue.

Customize the toolbar

Pass children to the preset toolbar to add, replace, or hide controls.

<DocxEditor.Toolbar>
  {/* Replace one control. */}
  <DocxEditor.Toolbar.Slot slotId="zoom.level">
    <ZoomPicker />
  </DocxEditor.Toolbar.Slot>

  {/* Remove one control. */}
  <DocxEditor.Toolbar.Bold hidden />

  {/* Remove a whole built-in group and its separator. */}
  <DocxEditor.Toolbar.Group id="zoom" hidden />
</DocxEditor.Toolbar>

Hiding a control does not disable its command or enforce permissions. The document state decides whether a command is available.

GoalAPI
Keep the default controlsDocxEditor.Toolbar
Change a control's appearanceclassName, icon, or asChild
Add controls that collapse responsivelyToolbar.Group with Toolbar.Action
Replace one controlToolbar.Slot with slotId
Remove a control or grouphidden on the part, Toolbar.Slot, or Toolbar.Group
Arrange selected controls onlypreset={false} and named toolbar parts
Use your own buttonsuseEditorCommand(slotId)
Change interface colors--doc-* CSS properties

For every slot ID and named part, see the Chrome slot reference. For frame layout, see React composition or Vue composition.

Add a command button

Call useEditorCommand() to connect a button to command state and execution.

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

function BoldButton() {
  const bold = useEditorCommand('text.bold');
  return (
    <button
      onMouseDown={(e) => e.preventDefault()} // chrome must not steal the caret
      onClick={() => bold.execute()}
      disabled={!bold.isEnabled}
      aria-pressed={bold.isActive}
      title={bold.disabledReason ?? 'Bold'}
    >
      Bold
    </button>
  );
}
MemberDescription
execute()Runs the command.
isEnabledWhether the command is available for the current selection.
isActiveWhether the command state is on, such as bold text.
disabledReasonThe engine's refusal reason, or null.

Custom buttons must call preventDefault() on mousedown to keep the selection. Skip it for INPUT, SELECT, and TEXTAREA elements, which need focus.

For the format painter, call the copyFormatting and pasteFormatting commands. pasteFormatting reports a disabled reason until something is copied.

Create and continue lists

Use the bullet or numbering button to create a list from selected paragraphs. The buttons create independent lists without reusing heading numbering elsewhere in the document.

A plain paragraph directly after a compatible list joins that list. A compatible list uses the selected marker kind at the joining level: bullets or numbers.

Selections containing both list items and plain paragraphs start a separate list by default. They join an ordinary list when they start with a plain paragraph directly after that list. They do not join heading numbering; selected numbered headings keep their style and level.

Paragraphs joining ordinary lists use the preceding item's level. For lists with style-linked levels, each joining paragraph's indent determines its level. A paragraph joining a level linked to a heading style takes that style.

If every selected paragraph already uses the selected list kind, the button removes list membership. For programmatic list creation and level settings, see Lists and numbering.

Keep host controls responsive

Wrap your controls in Toolbar.Group. The group collapses into the More (⋯) panel by priority, like the built-in groups.

<DocxEditor.Toolbar>
  <DocxEditor.Toolbar.Group id="review-nav" label="Review" priority={15}>
    <DocxEditor.Toolbar.Action label="Previous change" icon={<UpIcon />} onSelect={previous} />
    <DocxEditor.Toolbar.Action label="Next change" icon={<DownIcon />} onSelect={next} />
  </DocxEditor.Toolbar.Group>

  {/* A built-in id adds controls to that group. */}
  <DocxEditor.Toolbar.Group id="review">
    <DocxEditor.Toolbar.AddComment />
  </DocxEditor.Toolbar.Group>
</DocxEditor.Toolbar>
PropDescription
idA built-in id changes that group. Any other id creates a host group. contextual-table is reserved and ignored.
labelLiteral panel heading and accessible name for a host group. Wins over labelKey. Without either, the heading is id.
labelKeyCatalog key for the heading, translated through the toolbar t.
priorityCollapse priority. A lower number collapses first. With equal priorities, the group later in the bar collapses first.
pinnedtrue keeps the group in the bar at every width.
afterId of the group that this host group follows. Without it, host groups follow the built-in groups in declaration order. Groups with the same after keep declaration order.
hiddenRemoves the group from the bar and the panel.
overflowContentFunction that returns different content for the panel. On a built-in id, it renders after the group's own rows.

Built-in priorities are zoom 10, script 20, format 30, list 40, alignment 50, styles 60, font 70, text 80, and history 90. The review group is pinned. A host group without priority collapses before every built-in group.

In the panel, each Toolbar.Action becomes a full-width row with its icon and label. Other content renders as written.

Replace a control

Use Toolbar.Slot to put your content in a built-in control's position. It collapses with its group.

<DocxEditor.Toolbar>
  <DocxEditor.Toolbar.Slot slotId="zoom.level" overflowContent={() => <ZoomList />}>
    <ZoomPicker />
  </DocxEditor.Toolbar.Slot>
</DocxEditor.Toolbar>

To restyle a control without replacing it, pass className, icon, or asChild to its named part. Use named parts, such as FontColor, for controls that collect a value. Menu parts support the same slotId and hidden pattern.

Handle narrow layouts

The preset toolbar stays on one row. To size it to its content, use width: max-content; max-width: 100% in a block container or a single-line flex row. Collapsed groups return when the container grows.

The menu bar moves menus that do not fit into one ⋯ menu, from the end. Host children that are not menus stay in the bar.

  • In a flex row, give the menu bar min-width: 0 so it can shrink.
  • Set overflow={false} (:overflow="false" in Vue) to wrap the bar instead.

Limits

  • Loose children of Toolbar, outside a group, render after the groups and never collapse.
  • In the preset toolbar, each control sits in a .docx-toolbar__item wrapper. Target .docx-toolbar__group > .docx-toolbar__item > *, not direct children of the group. A toolbar with preset={false} has no wrapper.
  • Development builds warn about misplaced parts: an after that names no group, a built-in id with label or after, and a slot that the preset does not draw.
  • An after that names no group puts the host group at the end of the bar.
  • A Toolbar.Group with a built-in id ignores label, labelKey, and after.
  • A visible Toolbar.Slot that the preset does not draw, or that has an unknown slot id, renders after the groups.
  • pasteFormatting keeps the target run's shading (w:shd).
  • Toolbar.AddComment needs the review module and a registered comment UI. The review rail registers when it mounts. For your own UI, call useCommentDraftRequest.
  • Toolbar.Comments shows or hides the Comments & Changes pane.
  • The default toolbar shows the text direction parts, LeftToRight and RightToLeft, only when the interface direction (useChromeDir()) is 'rtl'. To show them in a left-to-right interface, add them to a custom toolbar. See Choose the interface direction.
  • For table controls and their limits, see Merge and split cells.

See also