Word fidelity
Word feature support matrix for the DOCX editor: what renders, what edits, what round-trips with structural fidelity, plus the security model and API stability policy.
Overview
- Browser editor use is client-side. Parsing, rendering, editing, and serialization run in the browser for normal
<DocxEditor>usage. The server-side editing API runs wherever you host it. .docxin,.docxout. The editor reads OOXML and writes OOXML. Untouched content, unsupported markup, and package payloads survive editing and save.- Apache 2.0, every published package except
@docx-editor.dev/editor-apiand@docx-editor.dev/pro, which are under the EigenPal Pro Evaluation License 1.0 (evaluate freely; production use needs a commercial agreement). SemVer, with the public API tracked by generated snapshots in CI. - Commercial support: docx-editor@eigenpal.com.
Feature matrix
Every feature is tracked on three axes:
- Editing: can a user (or code driving the editor) change it in the editor?
- Rendering: does it display the way Microsoft Word renders it?
- Round-trip: does it survive open, edit, save, and reopen without loss?
A construct the editor doesn't model is retained as inert content and written back with structural fidelity; that's the "Preserved" status below.
Legend: Full = works like Word · Partial = works with noted limits · Render only = displays correctly, not editable · Preserved = carried inertly through editing and save · Planned = on the roadmap · No = not supported.
Text & formatting
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Bold, italic, underline, strikethrough | Full | Full | Full | |
| Subscript & superscript | Full | Full | Full | |
| Font family & size | Full | Full | Full | Custom fonts registered via the fonts prop (loadFonts fetches and hash-verifies app-specified URLs); theme fonts resolved from the OOXML theme. Word-accurate wrap and pagination need font bytes for shaped measurement — the optional @docx-editor.dev/fonts package supplies metric-compatible substitutes for the Word defaults (Carlito, Caladea, Liberation). The fonts prop also accepts a resolver called once per load with the families a document declares, so an app can opt into loading only those; googleFonts() serves them from a pinned, hash-checked catalog. |
| Embedded fonts | Partial | Full | Preserved | Fonts embedded in the file (word/fonts) are de-obfuscated and wired into shaped text measurement automatically on load — no configuration or network. The embedded binaries round-trip on save; the editor does not add new embedded fonts. |
| Text color (RGB + theme colors) | Full | Full | Full | Theme color references (accent1...) round-trip as references, not flattened to hex. |
| Highlight & shading | Full | Full | Full | Word highlight palette plus arbitrary w:shd fills. |
| Right-to-left & bidirectional text | Full | Full | Full | Bidi layout with mirrored alignment; Hebrew locale ships in @docx-editor.dev/i18n. |
| Text effects (outline, shadow, emboss, emphasis mark) | No | Full | Full | w:outline, w:shadow, w:emboss, w:imprint and w:em render and round-trip; not settable from the toolbar. w14 glow and gradient text fill are not supported. |
| Hidden text (vanish) | No | Full | Full | w:vanish runs are not drawn and take no space, so pages break where Word breaks them; the text survives a round trip. There is no "show hidden text" view option, and a paragraph whose MARK is vanished still occupies a line. |
| Math equations (OMML) | No | Partial | Full | Equations round-trip verbatim (raw OMML) and show a styled text fallback. Laid-out math and equation editing are not built yet. |
| Symbol characters (w:sym) | Partial | Full | Full | Symbol runs render and survive ProseMirror edits and save. Symbols can be inserted from the Insert menu, but existing symbol-run properties are not directly editable. |
Paragraphs & styles
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Alignment & justification | Full | Full | Full | |
| Line & paragraph spacing | Full | Full | Full | Space before/after and line spacing (single, multiple, exactly, at least) all reach pagination, so a 1.5- or double-spaced document breaks pages where Word breaks them. Font external leading is excluded from line boxes, and trailing auto-spacing may cross the bottom text margin when the glyphs fit, matching Word’s vertical pagination. The paragraph mark’s w:sz participates in the last line’s metrics, matching Word when a cover-page mark is taller than the visible runs. Contextual spacing drops the gap between same-style neighbours, the way Word’s List Paragraph style intends. Automatic spacing (w:beforeAutospacing / w:afterAutospacing) replaces the authored measurement with 14pt in body paragraphs and 0pt in list items and table cells. |
| Indentation (incl. hanging indents) | Full | Full | Full | Left, right, first-line and hanging indents all reach line geometry, so an indented first line starts where Word starts it and wraps with the room it actually has. Increase/Decrease Indent is on the toolbar and on Tab / Ctrl+M; inside a list it changes the level, so the marker changes with it. |
| Paragraph styles (Heading 1, Quote, custom styles) | Full | Full | Full | Style picker applies document styles, including custom styles with their numbering and indents. Defining new styles in the UI is not supported yet. |
| Paragraph borders & fills | Partial | Full | Full | Paragraph shading (w:shd) is editable. Borders render common ST_Border line styles (single, double, dashed, dotted, and CSS approximations for thick/3-D/inset/outset); decorative art borders paint as a solid rule. Thin doubles inflate to a visible compound band in layout, matching table borders. Borders round-trip but cannot be added, changed or removed from the editor yet. |
| Tab stops & leaders | Partial | Full | Full | Existing tab stops render, including right/decimal tabs and dot, hyphen and underscore leaders. Positional tabs (w:ptab) render too, so a table-of-contents line reads as one: entry left, leader dots between, page number flush right. The document's own w:defaultTabStop is honoured, so a metric-locale grid lands where Word puts it, in headers and footers as well as the body. A tab-stop editing UI is not built yet. |
| Drop caps & text frames (framePr) | No | No | Full | Parsed and round-tripped; text flows inline rather than as a drop cap or positioned frame. |
| Automatic hyphenation | No | No | Preserved | Document hyphenation settings round-trip; the layout engine does not hyphenate. |
Lists & numbering
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Bullet lists (multi-level) | Full | Full | Full | Toolbar toggle creates the definition on first use, numbering.xml included, so a document that has never carried a list can start one. Tab and the indent buttons change the level, and the marker changes with it. |
| Numbered lists (decimal, roman, letters) | Full | Full | Full | |
| Custom numbering definitions & style-linked numbering | Full | Full | Full | Numbering attached to custom paragraph styles resolves with Word’s precedence rules. |
| List continuation & restart | Full | Full | Full | |
| Picture bullets (numPicBullet) | No | No | Preserved | Not rendered or editable; the numPicBullet definition and its authored markup are preserved on save. |
Tables
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Table insertion & cell editing | Full | Full | Full | |
| Row/column insert, delete, resize | Full | Full | Full | Core store and React paginated editor: hover row/column insertion, adjacent divider and outer-right resize, and seven table context-menu structural actions. Vue toolbar and context-menu value UI remain deferred; Vue inherits shared command types only. Tables remain read-only in the automation object model. |
| Cell borders & shading | Partial | Full | Full | Selected-cell borders and fill through React contextual toolbar controls (allowlisted styles, nullable clear fill). Vue value chrome deferred. Existing table/cell borders and table-style shading still render and round-trip. |
| Merged cells (horizontal & vertical) | No | Full | Full | Authored merges render and round-trip. Merge and split commands are declared but refused; column insert/delete/resize on merged tables shows the engine reason. |
| Tables split across pages | Full | Full | Full | Rows split mid-content with correct cut borders; vertically merged cells repaint on continuation pages like Word. |
| Nested tables | Partial | Full | Full | Innermost nested table owns resize furniture, structural edits, and selected-cell borders/fill in the React editor; outer tables stay isolated through save/reopen. Vue table chrome deferred. |
| Table styles & conditional formatting (header row, banding) | Partial | Full | Full | Table styles resolve through their basedOn chain: borders, cell margins, shading and the paragraph/run formatting a conditional format carries (so a header row comes out bold and centred) all come from styles.xml, gated by w:tblLook, with an explicit w:cnfStyle taking precedence. Conditional cell margins and switching table styles from the UI are not built yet. |
| Floating tables (tblpPr anchored position) | No | Partial | Full | An anchored table lands where Word puts it across the page: tblpXSpec/tblpX against the text, margin or page box, plus a tblpY offset from the text anchor. Text does not yet wrap beside it, and page- or margin-anchored vertical positions keep their place in the flow. |
| Vertical cell text (textDirection) | No | Full | Full | tbRl/btLr cell text renders via writing-mode and round-trips; not settable from the UI. |
Images & drawings
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Inline images (paste, drag-drop, resize) | Partial | Full | Full | Engine layout and paint for embedded PNG/JPEG/GIF at authored wp:extent; React insert/overlay authoring (toolbar, properties, keyboard resize). Vue authoring UI deferred to vue-drawing-authoring-parity — shared engine commands only. |
| Floating images & wrap modes (square, topAndBottom...) | Partial | Full | Full | Nine wrap choices, exclusion reflow, z-order, and anchored drag/resize in React. Vue wrap/alt/properties chrome deferred; engine setImageWrapType and toolbarCommandState are shared. |
| BMP and WebP images | No | Full | Full | Decoded natively by the browser and painted at the authored size, like PNG or JPEG. BMP covers what older documents carry (including top-down bitmaps and the 12-byte BITMAPCOREHEADER); WebP covers lossy, lossless and extended containers. Inserting a new one is not supported yet. |
| SVG images | No | Full | Full | Embedded SVG paints at the authored size. Rendered in the browser secure static mode, so scripts and external references inside the file stay inert. Inserting a new SVG is not supported yet. |
| WMF / EMF legacy vector images | No | Partial | Full | Rasterized in the browser and painted at the authored extent. A metafile that will not convert keeps its extent and a labelled placeholder. Original bytes round-trip untouched. |
| TIFF images | No | Partial | Full | Baseline TIFF is decoded in the browser and painted at the authored extent; the first page of a multi-page file is used. A flavour that will not decode keeps its extent and a labelled placeholder. Inserting a new TIFF is not supported yet. |
| Tracked image insert/delete | No | Preserved | Preserved | Revision wrappers preserved inertly; accept/reject and suggesting-mode delete owned by typed-revisions-and-comments. |
| Text boxes | No | Partial | Preserved | Anchored text boxes render their story content clipped inside the extent — in the body, headers, and footers, including page-relative anchors — with PAGE / NUMPAGES / SECTIONPAGES fields inside header/footer text boxes evaluated per page. Read-only: inner stories are not editable. Inline text boxes, linked chains, autofit, and rotation still render as placeholders or clip. |
| Drawing shapes & geometry | No | Partial | Preserved | Charts, groups, canvases, and custom geometry reserve extent with placeholders; unsupported payloads stay generic in the canonical tree. |
| Picture cropping (srcRect) | Partial | Full | Full | Crop renders and round-trips; React properties dialog edits crop in UI percent. Vue deferred. |
| Picture adjustments (brightness, contrast, recolor) | No | Partial | Full | Transparency, brightness, contrast and grayscale project where supported; authored adjustment markup is preserved on save. |
| Picture effects (shadow, glow, reflection) | No | No | Preserved | Not painted or editable; authored effect markup and effectExtent spacing are preserved. |
| Charts (DrawingML) | No | Partial | Preserved | Extent reserved with labelled placeholder; chart payload preserved generically, not semantically edited. |
| SmartArt & diagrams | No | Partial | Preserved | Same placeholder policy as charts; payload preserved inertly. |
| Ink annotations (w:ink) | No | No | Preserved | Not rendered or editable; ink markup is preserved generically on save. |
Page layout, headers & footers
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| True pagination (Word-metric pages) | Full | Full | Full | The layout engine paginates like Word: page breaks, keep rules, split paragraphs marked across pages. Hard page breaks are insertable and write `w:br w:type="page"`. |
| Sections (margins, size, orientation, per-section headers) | Partial | Full | Full | Page size, orientation and margins editable per section or whole document (Page Setup dialog, ruler drags); each section paginates against its own geometry, so mixed portrait/landscape documents render as Word shows them. Section breaks insertable. Even/odd-page break parity (the blank page Word inserts to reach the right parity) and per-section columns are not modelled yet. |
| Headers & footers (edit in place) | Partial | Full | Full | React: typed scoped header/footer editing (enter/exit story, create/remove, link/unlink to previous, title-page and even/odd options) with PAGE/NUMPAGES/SECTIONPAGES insert chrome. `editHeaderFooter` accepts `variant` / `evenPage` / `firstPage` on the shared Editor contract. Per-section first/even/default variants paint like Word. Vue chrome deferred; Vue can still call the shared commands. Tracked changes, watermark/drawing authoring, and structural table ops inside furniture are not claimed. |
| Watermarks (text & image) | No | Planned | Preserved | Watermarks live as VML/drawings inside header parts. Typing, layout, and editing are deferred to the drawings lane; Editor.getWatermark() is a stub. Structural markup may survive in the header part but is not a supported watermark feature. |
| Footnotes & endnotes | Partial | Partial | Full | React: typed note model, layout (pageBottom/beneathText/sectEnd/docEnd), scoped note editing, insert/delete/convert, chrome slots. Vue deferred. Tracked note inserts and notes-in-HF layout out of scope. |
| Multi-column layout | No | Partial | Full | Section w:cols count, gap, separator and equal/unequal widths paginate into columns; explicit column breaks leave the break paragraph's empty remainder at the top of the next column. Balancing continuous multi-column sections is supported. Column editing chrome is not exposed. |
| Page borders | No | Full | Full | Render with z-order, offset modes and first-page filters; not editable from the UI. |
| Line numbers (lnNumType) | No | No | Full | Parsed and round-tripped; not drawn in the margin. |
| Different even & odd headers | Partial | Full | Full | First, even, and default variants are selected by the page's number in the document (so the alternation carries across section breaks) and editable in an open furniture scope. Programmatic `editHeaderFooter({ variant: 'even' })` (or `evenPage: true`) creates/opens the even story and enables `w:evenAndOddHeaders` in one undo unit. React header/footer chrome can toggle different even and odd pages; Vue chrome deferred. |
| Section vertical alignment (vAlign) | No | No | Full | Round-trips; page content stays top-aligned. |
| Page background color/image (w:background) | No | No | Preserved | Not rendered or editable; authored background markup and relationships are preserved. |
| Page number format (pgNumType) | No | Partial | Full | Section numbering start, format, chapter style, and chapter separator parse and serialize. Allowlisted PAGE fields in headers/footers honour authored start and fmt (e.g. lowerRoman); NUMPAGES/SECTIONPAGES stay decimal. There is no pgNumType authoring UI yet. |
Review: tracked changes, comments, notes
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Tracked changes (insert, delete, format) | Full | Full | Full | Full revision model incl. structural changes (paragraph breaks, paragraph props, table rows/cells). Opens cleanly in Word’s review pane. |
| Accept / reject changes (UI + API) | Full | Full | Full | Per-change accept/reject in the sidebar and through acceptReviewItem/rejectReviewItem, plus revision.accept()/reject() and whole-document revisions.acceptAll()/rejectAll() through the automation object model. The sidebar itself offers no bulk control: resolve the queue with the per-item call over every item. |
| Comments (threads, replies, resolve) | Full | Full | Full | |
| Programmatic redlining (code-proposed tracked changes) | Full | Full | Full | Word-native tracked changes written through the automation object model, against DOCX bytes on a server or an editor open in a page. |
| Tracked moves (move from/to) | Partial | Full | Full | Imported moves render distinctly from insert/delete and round-trip. |
Fields, links & TOC
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Hyperlinks (external) | Full | Full | Full | Insert, edit and remove with Ctrl/Cmd+K or the toolbar. Targets are allowlisted (http(s), mailto, tel, ftp); anything else renders inert and still round-trips. Opening a document never requests a link target — activation is an explicit gesture. |
| Bookmarks & internal links | Partial | Full | Full | Internal links jump to their bookmark and move the caret, including targets on pages that have not been painted yet. Creating and renaming bookmarks is deferred. |
| PAGE / NUMPAGES / SECTIONPAGES fields | Partial | Full | Full | Allowlisted complex PAGE, NUMPAGES, and SECTIONPAGES project in headers/footers at layout time (PAGE respects section pgNumType start/fmt), including fields hosted inside anchored header/footer text boxes. Insertable from React header/footer chrome (including Page X of Y). Other field instructions stay inert; body field evaluation is deferred. |
| Table of contents | Partial | Full | Full | Body TOCs can be inserted from the shared Insert menu and refreshed from document headings, including page-numbers-only updates, tab leaders, section-formatted page numbers, and bookmark links. Generated rows are read-only navigation links. |
| Other field codes (DATE, REF, MERGEFIELD...) | No | Partial | Preserved | Last-computed field results display; the field codes themselves round-trip untouched. |
| Citations & bibliography | No | No | Preserved | CITATION/BIBLIOGRAPHY fields remain inert and the b:Sources store is preserved; citation evaluation and editing are not supported. |
| Legacy form fields (FORMTEXT, FORMCHECKBOX, FORMDROPDOWN) | No | Partial | Preserved | The field result shows as static text; w:ffData, including checkbox state and constraints, is preserved but the control is not interactive. |
Document structure & content controls
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Content controls (SDT): block, inline | Partial | Full | Full | Block, inline, row and cell controls are typed and addressable in every story (table cells, headers, footers and note bodies included); a control around a table row or cell lays out as that row or cell, keeping its grid column, span and row semantics. Discover, create, fill and remove them by tag, title or file id from the document object model; content is editable, and tag, title and lock are writable through the API but have no toolbar chrome. All four `w:lock` modes are enforced against what an edit would actually change — including the characters inside an inline control, a tracked-change decision, and a hyperlink write — an enclosing control’s lock wins over an inner one, and text typed at a control’s leading edge counts as inside it, because that is where Word puts it. A write addressed at one control is resolved against every control it would actually land in, so filling in an outer control cannot put text inside a locked or bound control nested at its edge — including an empty paragraph such a control holds, where the write has to create the run it lands in. Replacing a control’s whole value, or deleting a control together with its content, is refused when it would destroy a locked or bound control nested inside it; removing the wrapper while keeping the content leaves those controls untouched and is allowed. A control’s lock protects the control and its content, not the document: page setup, section furniture and note numbering stay editable beside a locked field. Under `w:documentProtection w:edit="forms"` only control content is editable, resolved from what an edit addresses — so an inline field can be filled in while the sentence around it stays read-only. Picture and repeating-section controls, custom-XML-bound controls and docPart galleries are preserved as they were rather than typed; every edit inside a bound control is refused instead of desynchronising it from its part, while removing the control is allowed and takes the binding with it. |
| Repeating section controls | Partial | Full | Full | Add and remove items from the editor; the section configuration itself is read-only. A repeating section is not typed as a content control in the document object model — it is preserved as authored, so a script reaches the controls inside it rather than the section itself. |
| Dropdown, checkbox & date controls | Full | Full | Full | Each control takes the value its own type accepts: a dropdown must name an item it declares, a combo box also takes free text, a date validates an ISO instant and writes both `w:fullDate` and the formatted text, and a checkbox writes its declared glyph and state together. A literal prompt is replaced whole on the first write; without a durable prompt source, clearing the value later leaves the control empty. A `w:temporary` control removes its own wrapper on the first edit and leaves the content. |
| Custom XML parts & data binding | No | No | Preserved | customXml parts and w:dataBinding round-trip with structural fidelity; no binding evaluation. |
| VBA macros | No | No | Preserved | Never executed, by design (client-side security); the vbaProject part survives open -> save. |
| OLE & embedded objects | No | No | Preserved | Never executed or rendered; OLE markup and embedded binary payloads are preserved through editing and save. |
| Document protection & editing restrictions | Partial | No | Preserved | Protection settings round-trip. Forms protection is enforced: only addressed content-control content remains editable, while the surrounding document stays read-only. Other protection modes are not enforced, and inline permission ranges may be dropped. |
Collaboration, i18n & editing UX
| Feature | Editing | Rendering | Round-trip | Notes |
|---|---|---|---|---|
| Realtime collaboration (Yjs) | Full | Full | Full | Live cursors, presence, comment sync, per-author tracked-change attribution. |
| Find & replace | Full | Full | Full | |
| Rich copy/paste (HTML clipboard) | Full | Full | Full | |
| Undo / redo | Full | Full | Full | |
| Editor UI in 9 languages | Full | Full | Full | en, de, fr, he, hi, pl, pt-BR, tr, zh-CN via @docx-editor.dev/i18n. |
| Document automation object model | Full | Full | Full | Batching object model shaped after a documented subset of the Word JavaScript API; server entry over bytes, browser entry over an open editor. No model integration, tool catalog or MCP transport ships with it. |
Rendering fidelity
The editor does not let the browser lay out the document. A DOM-free layout pass positions every line and page break from Word's own metrics (twips, the document's fonts and themes, its section and margin geometry) and the visible pages are painted from that result. The browser never decides where a line or page breaks; the layout pass does. The output is plain DOM text rather than a canvas bitmap.
How the pipeline achieves this is in Architecture. To judge fidelity yourself, open one of your own documents in the live demo next to Word.
Drawings, shapes and legacy images
These rows in the "Images & drawings" matrix are not Full yet, but their authored markup and payloads survive editing and save.
- Text boxes: story content renders read-only inside the authored extent; editing, linked chains, autofit, and rotation are not built yet.
- Drawing shapes: unsupported geometry reserves its extent with a placeholder; the original markup is preserved.
- WMF / EMF: rasterized where supported, otherwise shown as a placeholder; the original media bytes are preserved.
Round-trip behavior
A .docx file is a ZIP of XML parts, and Word writes a lot of XML the
editor has no reason to model: bookmarks, custom XML parts, mail-merge fields,
compatibility settings, VBA projects.
This editor's pipeline is parse → document model → edit → serialize. On save, the canonical tree preserves the structure and semantics of every parsed XML part, while package payloads pass through untouched. Concretely:
- Unmodeled XML elements and attributes are kept as generic nodes and re-emitted in place, including legacy VML, custom XML, and add-in markup.
- Non-XML payloads such as media, VBA projects, embedded fonts, and OLE binaries are copied through the ZIP untouched.
- References stay intact: relationship IDs, bookmark names, field codes, style IDs and numbering definitions are not renamed or renumbered.
- Output is canonical OOXML, not merely valid: tracked changes are real
w:ins/w:delrevisions Word's review pane understands, theme colors stay theme references, numbering keeps its definitions.
How it's tested: CI round-trips a corpus of real documents through two oracles, a canonical fingerprint over the tree and a save-and-reopen semantic digest; serializer tests assert specific constructs (revisions, fields, hyperlinks, content controls) survive; and browser end-to-end tests drive the painted editor.
The preservation contract is semantic and structural: content you did not edit must not be lost or reinterpreted. A document that breaks this behavior is a bug; attach it to a GitHub issue.
Security
- Normal editor use is browser-only. The editor package does not upload documents or call a conversion API.
- No macro execution. VBA macros and embedded code are not evaluated.
- The editing API is host-controlled.
@docx-editor.dev/editor-apiships no network transport and no model integration: the browser entry drives an editor already in the page and the DOCX never leaves it, and the server entry runs wherever you host it. What reaches a model is whatever your own code sends.
Bundle and performance
-coreships subpath exports that tree-shake independently.- The engine is a peer dependency of the adapters, so one copy resolves for the whole tree.
- Lazy-load the editor (
next/dynamic,React.lazy) to keep it out of the initial bundle. - The layout engine caches per-block measurements; an edit doesn't re-measure the document.
No published benchmarks; test with your own documents in the live demo.
Stability, license and support
- Apache 2.0, every package except
@docx-editor.dev/editor-apiand@docx-editor.dev/pro, which ship under the EigenPal Pro Evaluation License 1.0: free to evaluate internally, production use under a written agreement from licensing@eigenpal.com. - SemVer via changesets; all packages share one version number.
- The public API is tracked by API Extractor snapshots; CI fails on drift.
- Contributions need a one-time CLA.
If a feature you need is Partial or missing, open an issue
with a sample .docx, or email docx-editor@eigenpal.com
for priority features and support contracts.
Next steps
- Quickstart: the load, edit, save loop
- Architecture: the pipeline in depth
- Tracked changes: the review model in practice
Astro
Run an Astro DOCX editor as a React island with client:only. Why client:load crashes, how styles load, and the path from file upload to .docx download.
Examples
Runnable DOCX editor starters for Vite, Next.js, Remix, and Astro, plus a fully themed editor and a headless editing script. Clone one and run it.