GuideMarch 5, 20263 min read

Edit DOCX Files in the Browser with JavaScript

Parse, render, edit, and save Word documents in the browser with JavaScript. Uses the React adapter as the working example.

What the browser has to do

The .docx format is a ZIP archive containing XML files that follow the Office Open XML (OOXML) specification. Browser parsing requires four tasks:

  1. Unzip the archive with JavaScript.
  2. Parse the XML for document structure, styles, and relationships.
  3. Render paragraphs, tables, images, headers, and footers.
  4. Handle less common OOXML structures and relationships.

Use an editor package when your application needs this behavior. The rest of this guide uses docx-editor.

How client-side DOCX editing works

The flow has four stages:

.docx file (ArrayBuffer)
    ↓
OOXML Parser (unzip + XML parse)
    ↓
Document Model (paragraphs, tables, images, styles)
    ↓
ProseMirror Editor (WYSIWYG rendering)
    ↓
Export back to .docx (serialize + zip)

Parsing, editing, and serialization happen in the browser. Your app can keep contracts, medical records, financial statements, and other sensitive files on the client.

Quick start with React

The editor is a React component. Pass it the document bytes, and call save() on its ref to get the edited document back:

import { useRef } from "react";
import { DocxEditor, type DocxEditorRef } from "@docx-editor.dev/react";
import "@docx-editor.dev/core/styles/editor.css";
 
const DOCX_MIME =
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
 
function Editor({ buffer }: { buffer: ArrayBuffer }) {
  const editorRef = useRef<DocxEditorRef>(null);
 
  async function download() {
    const saved = await editorRef.current?.save();
    if (!saved) return;
 
    const blob = new Blob([saved], { type: DOCX_MIME });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "edited.docx";
    link.click();
    URL.revokeObjectURL(url);
  }
 
  return (
    <div style={{ height: "100vh" }}>
      <button onClick={download}>Download .docx</button>
      <DocxEditor ref={editorRef} document={buffer} />
    </div>
  );
}

save() returns Promise<ArrayBuffer | null>. The onSave prop is separate. It takes no arguments, and it reports only that the reader triggered a save from the menu.

For a Vue 3 setup, see Vue DOCX editor. For framework specifics, see the Next.js, Vite, Remix, and Astro guides.

Working with the OOXML format

The OOXML structure helps when you debug or extend the editor:

document.docx (ZIP archive)
├── [Content_Types].xml
├── _rels/.rels
├── word/
│   ├── document.xml      ← main body
│   ├── styles.xml         ← paragraph & character styles
│   ├── numbering.xml      ← list definitions
│   ├── header1.xml        ← headers
│   ├── footer1.xml        ← footers
│   ├── media/             ← embedded images
│   └── _rels/
│       └── document.xml.rels  ← relationships
└── docProps/
    ├── app.xml
    └── core.xml           ← metadata (author, dates)

The document.xml file contains the document body as XML elements like <w:p> (paragraph), <w:r> (run), <w:t> (text), <w:tbl> (table).

Supported features

Text formatting

The editor handles character formatting:

  • Font family and size
  • Bold, italic, underline, strikethrough
  • Text color and highlight color
  • Superscript and subscript
  • Character spacing

Tables

OOXML represents tables through nested grid, row, cell, and property elements. The editor supports:

  • Horizontal and vertical cell merging
  • Custom border styles per cell
  • Column widths
  • Cell shading and background colors

Images

A DOCX file stores embedded images in the word/media/ folder and references them through relationships. The editor renders inline images. It also positions floating images and wraps text around them.

Page layout

  • Page margins and size (Letter, A4, custom)
  • Headers and footers (different first page, odd/even)
  • Page breaks and section breaks
  • Columns

Security model

Editing in the browser reduces the document handling your server does:

  1. No required upload: documents stay in the browser unless your app uploads them
  2. No server temporary files: the editor does not require a server-side conversion step
  3. Smaller server attack surface: malformed documents are parsed client-side instead of in your backend
  4. Data residency control: you choose whether saved documents are downloaded locally or sent to your own API

Browser compatibility

The editor supports these browser versions:

  • Chrome 90+
  • Firefox 90+
  • Safari 15+
  • Edge 90+

It uses standard web APIs: ArrayBuffer, Blob, FileReader, and URL.createObjectURL. It needs no plugin.

Comparison with other approaches

ApproachProcessing locationDocument representationLicense
docx-editorBrowserOOXMLApache 2.0
Google Docs embedGoogle serversImported Google documentProprietary
LibreOffice WASMBrowserLibreOffice document modelMPL
Server-side conversionApplication serverConverted HTMLVaries

Where to go next