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><DocxEditorToolbar>
<!-- Replace one control. -->
<DocxEditorToolbar.Slot slot-id="zoom.level">
<ZoomPicker />
</DocxEditorToolbar.Slot>
<!-- Remove one control. -->
<DocxEditorToolbar.Bold hidden />
<!-- Remove a whole built-in group and its separator. -->
<DocxEditorToolbar.Group id="zoom" hidden />
</DocxEditorToolbar>Hiding a control does not disable its command or enforce permissions. The document state decides whether a command is available.
| Goal | API |
|---|---|
| Keep the default controls | DocxEditor.Toolbar |
| Change a control's appearance | className, icon, or asChild |
| Add controls that collapse responsively | Toolbar.Group with Toolbar.Action |
| Replace one control | Toolbar.Slot with slotId |
| Remove a control or group | hidden on the part, Toolbar.Slot, or Toolbar.Group |
| Arrange selected controls only | preset={false} and named toolbar parts |
| Use your own buttons | useEditorCommand(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>
);
}<script setup lang="ts">
import { useEditorCommand } from '@docx-editor.dev/vue';
const bold = useEditorCommand('text.bold');
</script>
<template>
<!-- chrome must not steal the caret -->
<button
@mousedown.prevent
@click="bold.execute()"
:disabled="!bold.isEnabled.value"
:aria-pressed="bold.isActive.value"
:title="bold.disabledReason.value ?? 'Bold'"
>
Bold
</button>
</template>The Vue composable returns computed refs on a plain object. Read them with .value.
| Member | Description |
|---|---|
execute() | Runs the command. |
isEnabled | Whether the command is available for the current selection. |
isActive | Whether the command state is on, such as bold text. |
disabledReason | The 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><DocxEditorToolbar>
<DocxEditorToolbar.Group id="review-nav" label="Review" :priority="15">
<DocxEditorToolbar.Action label="Previous change" @select="previous" />
<DocxEditorToolbar.Action label="Next change" @select="next" />
</DocxEditorToolbar.Group>
<!-- A built-in id adds controls to that group. -->
<DocxEditorToolbar.Group id="review">
<DocxEditorToolbar.AddComment />
</DocxEditorToolbar.Group>
</DocxEditorToolbar>| Prop | Description |
|---|---|
id | A built-in id changes that group. Any other id creates a host group. contextual-table is reserved and ignored. |
label | Literal panel heading and accessible name for a host group. Wins over labelKey. Without either, the heading is id. |
labelKey | Catalog key for the heading, translated through the toolbar t. |
priority | Collapse priority. A lower number collapses first. With equal priorities, the group later in the bar collapses first. |
pinned | true keeps the group in the bar at every width. |
after | Id 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. |
hidden | Removes the group from the bar and the panel. |
overflowContent | Function 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><script setup lang="ts">
import { h } from 'vue';
import { DocxEditorToolbar } from '@docx-editor.dev/vue';
import ZoomList from './ZoomList.vue';
import ZoomPicker from './ZoomPicker.vue';
const zoomList = () => h(ZoomList);
</script>
<template>
<DocxEditorToolbar>
<DocxEditorToolbar.Slot slot-id="zoom.level" :overflow-content="zoomList">
<ZoomPicker />
</DocxEditorToolbar.Slot>
</DocxEditorToolbar>
</template>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.
Menu bar overflow
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: 0so 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__itemwrapper. Target.docx-toolbar__group > .docx-toolbar__item > *, not direct children of the group. A toolbar withpreset={false}has no wrapper. - Development builds warn about misplaced parts: an
afterthat names no group, a built-in id withlabelorafter, and a slot that the preset does not draw. - An
afterthat names no group puts the host group at the end of the bar. - A
Toolbar.Groupwith a built-in id ignoreslabel,labelKey, andafter. - A visible
Toolbar.Slotthat the preset does not draw, or that has an unknown slot id, renders after the groups. pasteFormattingkeeps the target run's shading (w:shd).Toolbar.AddCommentneeds the review module and a registered comment UI. The review rail registers when it mounts. For your own UI, calluseCommentDraftRequest.Toolbar.Commentsshows or hides the Comments & Changes pane.- The default toolbar shows the text direction parts,
LeftToRightandRightToLeft, 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.