Patching
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:
| Type | Description |
|---|---|
"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: [
{
image: {
data: readFileSync("logo.png"),
width: "5.3cm",
height: "2.6cm",
type: "png",
},
},
],
},
}
Hyperlinks
Include hyperlinks in patch content:
placeholders: {
website: {
type: "paragraph",
children: [
{ text: "Visit " },
{
hyperlink: {
children: [{ text: "our website" }],
link: "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.
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 ) but omits the id — the patch assigns continuation ids automatically.
const result = await patchDocument({
outputType: "nodebuffer",
data: templateBuffer,
comments: {
paragraphs: {
0: [{ 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
| Option | Type | Default | Description |
|---|---|---|---|
outputType | string | — | Output format (see Export page) |
data | Buffer | Uint8Array | ... | — | Input .docx file data |
placeholders | Record<string, Patch> | — | Delimiter-wrapped placeholder name → patch content |
findReplace | Record<string, Patch> | — | Literal find string → patch content (no delimiters) |
coreProperties | Partial<CorePropertiesOptions> | — | Core metadata override, merged over existing values |
append | SectionChild[] | — | Block-level content appended before the final section break |
comments | { paragraphs?, placeholders? } | — | Inject comments anchored to paragraphs or placeholder runs (merged with existing) |
keepOriginalStyles | boolean | true | Preserve original run formatting properties |
placeholderDelimiters | { start: string, end: string} | { start: "{{", end: "}}" } | Custom placeholder delimiters |
recursive | boolean | true | Replace all occurrences (not just the first) |
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.
placeholdersandfindReplaceshare one engine; combine both in a single call.- Use
keepOriginalStyles: true(default) to inherit the template's run formatting when replacing text. recursive: true(default) replaces all occurrences of each placeholder; set tofalseto replace only the first.- Images and hyperlinks in patch content are automatically added to the document's relationships.