Composables
Vue composables used by the packaged chrome: subscribe to editor state, run commands, read the document outline, search, and drive page setup.
Call each composable during setup() inside DocxEditor.Root. The packaged
<DocxEditor> also renders this root.
These composables return a ref, or a plain object that holds refs. A template
unwraps a ref only when the ref is a top-level binding. A ref that is a property
of a returned object stays a ref, so read it with .value in the template and in
script code.
useEditorCommand
Pass a chrome slot ID to get its action and reactive command state. The chrome slot reference lists every available ID and its named toolbar part:
<script setup lang="ts">
import { useEditorCommand } from '@docx-editor.dev/vue';
const bold = useEditorCommand('text.bold');
</script>
<template>
<button
@mousedown.prevent
:disabled="!bold.isEnabled.value"
:data-active="bold.isActive.value || undefined"
:title="bold.disabledReason.value ?? 'Bold'"
@click="bold.execute()"
>
B
</button>
</template>execute() returns true when the editor applies the command. isActive,
isEnabled, and disabledReason are computed refs on a plain object. The
template does not unwrap them, so read each one with .value.
The engine is the only source for enabled state. Show disabledReason when a
control cannot run.
You can also pass a reactive slot ID or an EditorCommand:
import { computed } from 'vue';
import { useEditorCommand } from '@docx-editor.dev/vue';
const mode = computed(() => ({
type: 'setEditingMode' as const,
mode: 'suggesting' as const,
}));
const suggest = useEditorCommand(mode);useEditorState
Subscribe to one slice of the editor snapshot. The composable returns a read-only shallow ref.
<script setup lang="ts">
import { useEditorState } from '@docx-editor.dev/vue';
const page = useEditorState((s) => s.page);
const dirty = useEditorState((s) => s.canUndo ?? false);
</script>
<template>
<span>{{ page.current }} / {{ page.total }}</span>
<button :disabled="!dirty">Save</button>
</template>The selector runs after editor updates. The ref changes only when the selected value changes.
Pass a comparator when you select an object:
import { useEditorState } from '@docx-editor.dev/vue';
const formatting = useEditorState(
(snapshot) => snapshot.formatting,
(left, right) => left?.bold === right?.bold && left?.italic === right?.italic
);Select the smallest useful value. A page selector then stays stable when the user changes bold formatting.
Useful snapshot fields include page, selection, selectionCollapsed,
formatting, table, image, editable, isLoading, isOpening,
parseError, editingMode, canUndo, canRedo, pageSetup,
fontSubstitutions, hasReviewContent, and lastRejection.
useDocxEditor
This composable returns a shallow ref to the editor instance. Its value is
null before the DocxEditor.Root mount effect creates the instance, and
outside a DocxEditor.Root.
<script setup lang="ts">
import { useDocxEditor } from '@docx-editor.dev/vue';
const editor = useDocxEditor();
async function save() {
const bytes = await editor.value?.save();
if (bytes) void upload(bytes);
}
</script>Use the instance for actions and one-time reads. A direct snapshot() call
does not create a Vue subscription. Use useEditorState for reactive state.
useEditorEvent
Subscribe to an editor event for the component lifetime:
import { useEditorEvent } from '@docx-editor.dev/vue';
useEditorEvent('selectionChange', () => {
panelOpen.value = false;
});
useEditorEvent('change', (change) => {
void autosave(change.revision);
});useEditorCaret
Returns the caret as { paragraphId, offset }. Write APIs accept this shape as
their at value.
useEditorSnapshot
Returns a revision ref for an editor. Use it for a custom external-store subscription.
useZoom
Returns the current zoom and controls that change it.
useFontFamily
Returns the current family, available families, setter, and enabled state:
<script setup lang="ts">
import { useFontFamily } from '@docx-editor.dev/vue';
const font = useFontFamily();
</script>
<template>
<select
:value="font.value.value ?? ''"
:disabled="!font.isEnabled.value"
@change="font.setValue(($event.target as HTMLSelectElement).value)"
>
<option v-for="family in font.options.value" :key="family" :value="family">
{{ family }}
</option>
</select>
</template>The current family is on the value member, and that member is a computed
ref. Read it as font.value.value.
useParagraphStyle provides the same pattern for paragraph styles.
usePageSetup
Read and change page size, margins, orientation, and scope:
<script setup lang="ts">
import { computed } from 'vue';
import { usePageSetup } from '@docx-editor.dev/vue';
const page = usePageSetup();
const landscape = computed(() => page.pageSetup.value?.orientation === 'landscape');
function toggleOrientation() {
page.apply({
orientation: landscape.value ? 'portrait' : 'landscape',
scope: 'section',
});
}
</script>
<template>
<button :disabled="!page.isEnabled.value" @click="toggleOrientation">
{{ landscape ? 'Use portrait' : 'Use landscape' }}
</button>
</template>apply() returns true when the editor applies the update. Sizes and margins
use twips. Set scope to 'section' or 'document'.
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. Read the returned refs with .value:
<script setup lang="ts">
import { computed } from 'vue';
import { useParagraphFormat } from '@docx-editor.dev/vue';
const { format, apply, isEnabled } = useParagraphFormat();
const isDouble = computed(() => format.value?.lineSpacing?.value === 2);
function toggle() {
apply({ lineSpacing: { rule: 'multiple', value: isDouble.value ? 1 : 2 } });
}
</script>
<template>
<button :disabled="!isEnabled" @click="toggle">
{{ isDouble ? 'Single space' : 'Double space' }}
</button>
</template>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, DocxEditorParagraphDialog is the Paragraph dialog over
this composable.
useParagraphIndent
Returns current paragraph indents and an apply() action for ruler controls.
useDocumentOutline
Read headings in document order and move the selection to a heading:
<script setup lang="ts">
import { useDocumentOutline } from '@docx-editor.dev/vue';
const outline = useDocumentOutline();
</script>
<template>
<p v-if="outline.isEmpty.value">No headings</p>
<ul v-else>
<li
v-for="{ heading, depth } in outline.items.value"
:key="heading.blockId"
:style="{ paddingInlineStart: `${depth * 12}px` }"
>
<button
:aria-current="heading.blockId === outline.selectedBlockId.value ? 'location' : undefined"
@click="outline.goTo(heading.blockId)"
>
{{ heading.text }}
</button>
</li>
</ul>
</template>Each item has { heading, depth }. A heading has text, level, and
blockId. depth starts at zero for the shallowest heading in the document.
Use headings when you do not need the calculated depth.
useDocumentSearch
Search the document and move between matches:
<script setup lang="ts">
import { useDocumentSearch } from '@docx-editor.dev/vue';
const search = useDocumentSearch();
</script>
<template>
<label>
Find
<input
:value="search.query.value"
@input="search.setQuery(($event.target as HTMLInputElement).value)"
/>
</label>
<span aria-live="polite">
{{ search.matches.value.length === 0 ? 0 : search.activeIndex.value + 1 }}
/ {{ search.matches.value.length }}{{ search.truncated.value ? '+' : '' }}
</span>
<button :disabled="search.matches.value.length === 0" @click="search.previous">Previous</button>
<button :disabled="search.matches.value.length === 0" @click="search.next">Next</button>
</template>The composable waits 150 milliseconds before it searches. isPending reports
that delay. It also provides goTo, clear, matchCase, setMatchCase,
wholeWord, and setWholeWord. truncated becomes true at the 2,000-match
limit.
useNavigationPane
Returns the navigation pane state, width, active tab, and setters.
useNavigationShift
Returns the horizontal page offset caused by the open navigation pane.
useReviewGutter
Returns the inline reservations for the active review rail. The full card column changes to compact marker strips on narrow viewports.
useDocxSource
Fetch bytes and fonts from a URL string, a URL, a Uint8Array, or an
ArrayBuffer:
const { document, fonts, error, isLoading } = useDocxSource('/sample.docx', {
fonts: packagedFonts(),
});fonts takes one origin or a list of them in precedence order, such as
[packagedFonts(), googleFonts()]. With an on-demand resolver, document is
released as soon as the bytes arrive. With an eager loader such as
defaultFonts, it waits for the fonts first.
useFonts
Builds one FontResolver from any number of font origins, first-wins in
argument order. See the font guide.
useHyperlinkPopup and useHyperlinkPopupInstance
Returns the link popover state and actions. Use
useHyperlinkPopupInstance outside the component that owns the popover
context.
useContentControl and useContentControlInstance
Returns content-control locks, value actions, and form-fill state. Use
useContentControlInstance outside the component that owns this context.
useHeaderFooterState
Returns the active header or footer scope, or null.
useNoteScopeState and useNotePropertiesState
Return the active note scope and its numbering properties.
useContextMenuTarget
Returns the element that received the last context-menu action.
useEditorValueCommand
Returns state and actions for value commands such as 'image.wrap' and
'image.altText'.
useTableBorderTargetLabel
Returns the label for the active table border target.
useTranslation and useChromeTranslate
useTranslation returns { t } for the active locale. useChromeTranslate
resolves labels for chrome t props. Pass a Map to override selected labels.
Toolbar helpers
useToolbarContext returns the toolbar compound context.
useToolbarLabel returns the active scope label. useToolbarLabelFor resolves
a label for one slot ID. useScopeClassName returns the chrome class prefix.
useScopedChromeAnchor returns anchor data for overlay chrome.
Review author styles
useReviewAuthors returns a shallow ref with each review author's author,
slot, color, and resolved style. Tracked-change authors come first.
Comment-only authors follow them.
Declare styles inside DocxEditor.Root:
<script setup lang="ts">
import { DocxEditor, useReviewAuthors } from '@docx-editor.dev/vue';
const authors = useReviewAuthors();
</script>
<template>
<DocxEditor.AuthorStyle
author="Ada"
color="var(--doc-review-author-1)"
avatar-url="/avatars/ada.png"
/>
<p>{{ authors.length }} review authors</p>
</template>Use DocxEditor.ColorByChangeType to color insertions and deletions by change
type. These components change presentation only. They do not change the DOCX
author data.
Pro Vue composables
Install @docx-editor.dev/pro and import these APIs from
@docx-editor.dev/pro/vue:
useReview()returns items, active state, review actions, pane state, and readiness.useReviewOf(editorRef, query?)binds the same state to an explicit editor ref.useReviewItem()returns the item for the active review card context.useReviewAuthor(author)returns the resolved author style in a review rail.useStackedReviewPositions(items, heights, options?)calculates card positions without overlap.
The package also exports DocxEditorReview. Mount it inside
DocxEditor.Viewport, beside DocxEditor.Content.
Next steps
- Vue composition: place custom controls.
- Chrome slot reference: find every slot ID and toolbar part.
- Vue API reference: inspect all composable types.