DOCX

Patching

Modify existing .docx files by replacing placeholders, literal text, or metadata

Patch an existing .docx template in three ways: replace {{placeholder}} tokens, find-and-replace literal text, or override document metadata. Patch content can be inline runs, block-level elements (paragraphs, tables), images, and hyperlinks.

patchDocument

Replaces placeholders in an existing .docx file:

import { patchDocument } from "@office-open/docx";
import { readFileSync, writeFileSync } from "node:fs";

const result = await patchDocument({
  outputType: "nodebuffer",
  data: readFileSync("template.docx"),
  placeholders: {
    name: {
      type: "paragraph",
      children: [{ text: "John Doe" }],
    },
  },
});

writeFileSync("output.docx", result);

Patch types

Each patch is discriminated by a bare type string literal:

TypeDescription
"paragraph"Replace the placeholder with inline run-level content
"document"Replace the placeholder with block-level content

"paragraph"

Replaces the placeholder text inside a paragraph with new runs. The original run's formatting properties (font, size, color, bold, etc.) are preserved by default.

{
  "placeholders": {
    "title": {
      "type": "paragraph",
      "children": [
        {
          "text": "Hello ",
          "bold": true
        },
        {
          "text": "World"
        }
      ]
    }
  }
}

"document"

Replaces the placeholder with block-level elements (paragraphs, tables, etc.). The surrounding context is preserved.

placeholders: {
  content: {
    type: "document",
    children: [
      { paragraph: { children: ["First paragraph"] } },
      { paragraph: { children: ["Second paragraph"] } },
      {
        table: {
          rows: [
            {
              cells: [
                { children: [{ paragraph: { children: ["Cell"] } }] },
              ],
            },
          ],
        },
      },
    ],
  },
}

Images

Replace a placeholder with an image:

placeholders: {
  logo: {
    type: "paragraph",
    children: [
      {
        picture: {
          type: "png",
          data: readFileSync("logo.png"),
          transformation: { width: "5.3cm", height: "2.6cm" },
        },
      },
    ],
  },
}

Include hyperlinks in patch content:

{
  "placeholders": {
    "website": {
      "type": "paragraph",
      "children": [
        {
          "text": "Visit "
        },
        {
          "hyperlink": {
            "children": [
              {
                "text": "our website"
              }
            ],
            "url": "https://example.com"
          }
        }
      ]
    }
  }
}

Find and Replace

Replace literal text without any delimiters — the keys are matched verbatim. Useful for rebranding or updating fixed wording. Values use the same Patch shape (paragraph runs or block content):

const result = await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  findReplace: {
    "Acme Corp": { type: "paragraph", children: [{ text: "Globex", bold: true }] },
    Draft: { type: "paragraph", children: [{ text: "Final" }] },
  },
});

Core Properties

Override document metadata (docProps/core.xml). Values are merged over the existing core properties — supply only the fields you want to change:

const result = await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  coreProperties: { title: "Quarterly Report", creator: "Jane Doe" },
});

Append Content

Append block-level content to the document body, inserted before the final section break (<w:sectPr>). It reuses the same SectionChild vocabulary as "document" patches, so paragraphs, tables, images, and hyperlinks are all supported:

const result = await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  append: [
    { paragraph: { children: ["Appended paragraph"] } },
    { paragraph: { children: [{ text: "Bold tail", bold: true }] } },
  ],
});

Appended content is serialized through the same compile-path stringifiers as generateDocument, and its images and hyperlinks are wired into the document's relationships automatically. Styles and numbering referenced by appended content must already exist in the template.

Section Edits

Edit the section list itself — replace a whole section's content or append new sections. Each replacement entry names its 0-based section index (0 = from the document start):

const result = await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  sections: {
    replace: [
      {
        index: 0,
        section: {
          children: [{ paragraph: { heading: "Heading1", children: ["Chapter 1 — replaced"] } }],
        },
      },
    ],
    append: [
      {
        children: [{ paragraph: { children: ["Appendix"] } }],
        properties: { pageSize: { width: 16838, height: 11906 } },
      },
    ],
  },
});
  • Without properties, a replaced section keeps its existing page setup; with properties, the new page setup takes over.
  • Appended sections are inserted before the final body sectPr, each closed by its own section break. A section without properties inherits the final section's page setup.
  • Children serialize through the same pipeline as append (paragraphs, tables, images, hyperlinks).
  • headers/footers are not supported in patched-in sections — they would need new parts; use generateDocument for those.

Comments

Inject comments into an existing document, merged with any existing word/comments.xml. Comment ids are continued from the highest existing id (or 0 when there are none). Two anchor kinds:

  • paragraphs — wrap a comment around the Nth body paragraph (0-based, by document order).
  • placeholders — wrap a comment around the run containing a {{key}} token, applied before the placeholder is substituted.

Each comment reuses the CommentOptions vocabulary (see Comments and Revisions) but omits the id — the patch assigns continuation ids automatically.

const result = await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  comments: {
    paragraphs: [
      {
        index: 0,
        comments: [{ author: "Alice", children: ["Review the opening paragraph."] }],
      },
    ],
    placeholders: {
      name: [{ author: "Bob", children: ["Verify this value."] }],
    },
  },
});

Existing comments are preserved — new entries are appended to word/comments.xml and re-serialized, and the comments relationship and content-type wiring are added when the part is new.

Custom Delimiters

Default delimiters are {{ and }}. Change them with placeholderDelimiters:

await patchDocument({
  outputType: "nodebuffer",
  data: templateBuffer,
  placeholders: { name: { type: "paragraph", children: [{ text: "John" }] } },
  placeholderDelimiters: { start: "<<", end: ">>" },
});

Options

OptionTypeDefaultDescription
outputTypestringOutput format (see Export page)
dataBuffer | Uint8Array | ...Input .docx file data
placeholdersRecord<string, Patch>Delimiter-wrapped placeholder name → patch content
findReplaceRecord<string, Patch>Literal find string → patch content (no delimiters)
corePropertiesPartial<CorePropertiesOptions>Core metadata override, merged over existing values
appendSectionChild[]Block-level content appended before the final section break
sections{ replace?, append? }Section edits: replace is an array of { index, section } entries
comments{ paragraphs?, placeholders? }Inject comments anchored to paragraphs or placeholder runs (merged with existing)
keepOriginalStylesbooleantruePreserve original run formatting properties
placeholderDelimiters{ start: string, end: string}{ start: "{{", end: "}}" }Custom placeholder delimiters

patchDetector

Scan a template to discover all placeholder keys before patching:

import { patchDetector } from "@office-open/docx";

const placeholders = await patchDetector({
  data: readFileSync("template.docx"),
});
// ["name", "title", "content", ...]

Tips

  • Placeholders span across split runs in Word — the library handles this automatically.
  • placeholders and findReplace share one engine; combine both in a single call.
  • Every occurrence of a placeholder is replaced, not just the first — in the body, headers, and footers alike.
  • Use keepOriginalStyles: true (default) to inherit the template's run formatting when replacing text.
  • Images and hyperlinks in patch content are automatically added to the document's relationships.
Copyright © 2026