Images and drawings
Insert, read, and edit DOCX pictures and text boxes from React or Vue, and replace unsupported drawing previews.
The editor displays inline and floating DrawingML pictures (w:drawing with pic:pic). React and Vue ship controls to insert pictures, change wrapping, resize, and edit alternative text. Saving preserves the picture markup and media.
Insert an image
Check the bytes with normalizeImageBytes, then run insertImage. Import from @docx-editor.dev/vue in Vue.
import { normalizeImageBytes } from '@docx-editor.dev/react';
const result = normalizeImageBytes(bytes);
if (result.ok) {
await editor.executeImageCommand({
type: 'insertImage',
data: result.bytes,
mime: result.mime,
widthPoints: result.widthPoints,
heightPoints: result.heightPoints,
});
}widthPoints and heightPoints are maximums. An image that does not fit its cell, column, or page scales down proportionally. Read the committed size from snapshot().image.
| Format | Insert | Display |
|---|---|---|
| PNG, JPEG, GIF | Yes | Yes |
| BMP, WebP, SVG | No | Yes, at the authored wp:extent |
| TIFF, EMF, WMF | No | Rasterized when conversion succeeds, otherwise a labeled placeholder |
External r:link or TargetMode="External" | No | Placeholder; never loaded automatically |
- The editor refuses insertion while another picture is selected. Place the caret in text first. The toolbar shows this reason when it disables insertion.
- PDF export of EMF and WMF needs the drawing module. For more information, see Advanced drawing rendering.
- To use your own image decoder, pass an
ImageDecodePortthrough theimageDecodePortprop onDocxEditoror the adapter's root component.
Read and change an image
Read the selected image from the editor state. In Vue, useEditorState returns a ShallowRef.
import { useEditorState } from '@docx-editor.dev/react';
const image = useEditorState((s) => s.image); // SelectedImageState | nullOutside a component, call editor.getSelectedImage() or read editor.snapshot().image. Both return the same SelectedImageState.
| Field | Meaning |
|---|---|
widthEmu, heightEmu | Extent in EMUs |
crop | Crop inset per edge, in percent (0 to 100) |
wrap | Wrap target (see the next table) |
position, rotationDegrees, locks, hidden | Placement and lock state |
title, description, hyperlink | Alternative text and link |
resourceStatus | pending, ready, unrenderable, external, or missing |
canResize, canMove | False for locked or hidden drawings, rotated legacy VML text boxes, and viewing mode. canMove is also false for inline drawings. |
canChangeWrap, canCrop | True for pictures only, never text boxes |
Check canResize, canMove, canChangeWrap, and canCrop before you offer the matching control.
wrap value | OOXML |
|---|---|
inline | wp:inline |
square, squareLeft, squareRight | wrapSquare with bothSides, left, or right |
tight, through | wrapTight, wrapThrough |
topAndBottom | wrapTopAndBottom |
behind, inFront | wrapNone with @behindDoc 1 or 0 |
This example wraps text around the selected picture:
if (editor.can({ type: 'setImageWrapType', target: 'square' }).ok) {
editor.exec({ type: 'setImageWrapType', target: 'square' });
}| Command | Effect |
|---|---|
setImageWrapType | Changes the wrap target |
setImagePosition | Moves a floating drawing, in EMUs |
setImageProperties | Changes size, crop, alternative text, hyperlink, and border |
transformImage | Rotates or flips: rotateCW, rotateCCW, flipH, flipV |
replaceImage | Replaces the picture bytes through executeImageCommand |
deleteImage | Deletes the picture |
Each completed drag or resize gesture is one undo step. Alternative text reads @descr, then @title. A picture with neither is decorative.
Configure image controls
Both adapters export the same parts. The image toolbar group appears when a picture is selected.
| Slot | Component or hook |
|---|---|
image.insert | ImageInsertProvider, ImageInsertTrigger |
image.wrap | ImageWrap, useEditorValueCommand('image.wrap') |
image.altText | ImageAltText |
image.properties | ImagePropertiesTrigger, DocxEditorImagePropertiesDialog |
The packaged Insert menu has an Image row. Hide or move it through DocxEditor.Menu.ImageInsert. For layout and overrides, see Toolbar customization and Customize popups.
Edit text boxes
Run insertTextBox with the caret in a body paragraph. The editor adds an empty rectangular box and places the caret in it.
if (editor.can({ type: 'insertTextBox' }).ok) {
editor.exec({ type: 'insertTextBox' });
}The toolbar slot is insert.textBox. Insertion in tables, headers, and footers is unavailable.
- Click a body text box to edit it. Drag the frame to move an anchored box, or drag its handles to resize. An inline box resizes but moves only with its line.
- Double-click a header or footer text box to open the header or footer with the caret in the box. Press Escape once to select the box and twice to close the header or footer.
- Pasting into a text box inserts plain text. Table insertion is unavailable inside a text box.
- Rotated or vertical text, and boxes that hold a table, take no caret. Clicking selects the box and its frame states the reason.
- Rotated legacy VML boxes, and boxes sized as a percentage of the page or margin, cannot move or resize.
- Find and
useDocumentSearchsearch text boxes in the body, headers, and footers, not in notes.
Custom unsupported-content components
Register a component to replace the placeholder for unsupported content, a drawing, or an equation. Match on the OOXML namespace and element name.
<DocxEditor document={bytes}>
<DocxEditor.UnsupportedElement
match={(element) => element.namespaceUri === 'urn:my-widgets'}
layout={{ width: 180, height: 48 }}
>
{({ element }) => <MyUnsupportedContent element={element} />}
</DocxEditor.UnsupportedElement>
</DocxEditor><DocxEditor :document="bytes">
<DocxEditor.UnsupportedElement
:match="(element) => element.namespaceUri === 'urn:my-widgets'"
:layout="{ width: 180, height: 48 }"
>
<template #default="{ element }">
<MyUnsupportedContent :element="element" />
</template>
</DocxEditor.UnsupportedElement>
</DocxEditor>| Prop | Description |
|---|---|
match | Selects elements. Omit it for a catch-all. The first mounted matching declaration wins. Prop updates keep its priority; a remounted declaration moves last. |
layout | Width and height in points for opaque content: finite, positive, and at most 100,000. Defaults: 120 × 24 inline, 240 × 96 block. Drawing and equation placeholders keep their own size. |
includePartial | Also matches partially rendered drawings. Default false. |
The UnsupportedElement record has these fields:
| Field | Description |
|---|---|
nodeId, ownerPartName | Element identity and its document part. For mc:AlternateContent, nodeId is the drawing. |
namespaceUri, localName | OOXML element name. For mc:AlternateContent, the wrapper, which is also source. |
source | Read-only canonical source, not an XML string. Treat its values as untrusted. null when a standalone painter has no source lookup. |
placement | inline or block |
label | Text of the built-in placeholder |
drawing, equation | Optional engine metadata, such as support, drawingIssues, graphicKind, and drawing.chart |
bounds | Optional box after layout |
- To keep the built-in placeholder, return empty content from the render function or slot itself:
null,false, an empty fragment, or whitespace. The editor does not try the next declaration. - A returned component that later renders
nullstill replaces the placeholder, with an empty box. - Components can unmount when their page leaves the viewport. Keep durable state in your application.
- Custom components do not change the saved DOCX or PDF export.
- The component slot stops keyboard, clipboard, context-menu, and drop events from reaching the editor.
- Properties, metadata, and the internal content of a drawing, equation, or field are never separate replacement targets.
- To match chart and DrawingML diagram placeholders, see Replace a placeholder. That recipe works without the EigenPal Pro License.
Replace a partially rendered drawing
Set includePartial to show your own preview for drawings that render without some features. Shapes with editable text, fully supported drawings, hidden drawings, and drawings without a layout position stay excluded.
<DocxEditor document={bytes}>
<DocxEditor.UnsupportedElement includePartial match={(element) => !!element.drawing}>
{({ element }) => <DrawingPreview element={element} />}
</DocxEditor.UnsupportedElement>
</DocxEditor>In Vue, set :include-partial="true". Size the preview to element.bounds. A click on the preview selects the drawing.
Preview with your rendering service
Call drawingPreviewDocx() to create a DOCX that holds one drawing, including unsaved edits. Send it to your own renderer.
import { drawingPreviewDocx } from '@docx-editor.dev/core/editor';
const result = drawingPreviewDocx(editor, element);
if (result.ok) {
// Send result.docx, result.pageFitsDrawing, and element.bounds to your renderer.
}The editor never uploads documents or calls a rendering service. On a server, use inspectDrawingSupport() and createDrawingPreviewPackage() from @docx-editor.dev/core/store.
Failure reason | Cause |
|---|---|
drawing-not-found | The drawing is not in the package |
too-many-parts | More than 64 copied parts, by default |
too-large | More than 64 MiB, by default |
- Use the
limitsargument to change the defaults. The preview excludes external relationships, embedded objects, macros, and templates. - If
pageFitsDrawingistrue, use the first page. Otherwise crop from(0, 0)tobounds.width * dpi / 72bybounds.height * dpi / 72. Withoutbounds, trim the white space around the drawing. - Run the renderer without network access and with time and memory limits. Treat the DOCX and returned images as untrusted.
- Show returned images through an image element. Never insert returned SVG or HTML strings into the page.
- Cache previews outside the component, for example in a
WeakMapkeyed byelement.source. Cancel stale requests. Invalidate previews when your application changes related chart data, diagram data, or themes. drawingPreviewDocx()is synchronous. Call it in an effect or event handler, and queue large batches.inspectDrawingSupport()covers body, header, footer, footnote, and endnote drawings. Ifreport.completeisfalse, the inventory is incomplete. It skips chart data, diagram data, inherited theme effects, and image bytes, so an empty issue list does not prove full support.- Pass an inspection entry with the same package object to
createDrawingPreviewPackage(). Node IDs do not persist in saved XML.
Limits
- External images never load automatically. Open, layout, paint, and save make no network request. Only an explicit user action embeds an external target.
- Embedded image bytes pass signature checks and size limits before decoding. SVG paints in a static image mode: its scripts never run and its external references never load.
- Drawing hyperlinks (
a:hlinkClick) need an explicit gesture and pass throughsanitizeHref. - With tracking on, image insertion and deletion record tracked changes. Property edits (size, position, wrap, crop, alt text, and links) apply directly, because they have no tracked form. Replacing a picture is refused.
- PDF export does not apply brightness, contrast, grayscale, or black-and-white adjustments.
- Picture commands do not address members of a shape group.
- Copy and cut refuse selections that contain a chart or an embedded object.
- Charts, DrawingML diagrams without a saved drawing, and drawing effects need the drawing module. Core renders saved diagram drawings. For the module, see Advanced drawing rendering.
- Wrap geometry, VML previews, shape groups, and alternative content follow the rules in DOCX feature support.