Headless processing
Parse, inspect, mutate, and serialize DOCX files in Node.js or workers with @eigenpal/docx-editor-core/headless. No DOM and no editor UI required.
@eigenpal/docx-editor-core/headless is the editor's document engine without the editor: the OOXML parser, the document model, mutation helpers, and the serializer. It runs in Node.js and web workers. Nothing in it touches the DOM.
Use it for batch pipelines: fill templates, stamp watermarks, extract text, generate documents, and post-process uploads on the server. The model it produces is exactly what the live editor renders, so logic written headless ports unchanged when the same buffer reaches a <DocxEditor>.
npm install @eigenpal/docx-editor-coreParse, walk, serialize
import { readFile, writeFile } from "node:fs/promises";
import {
parseDocx,
getParagraphs,
getParagraphText,
repackDocx,
} from "@eigenpal/docx-editor-core/headless";
const buffer = await readFile("contract.docx");
const doc = await parseDocx(buffer);
// The body lives at doc.package.document; helpers save you the tree-walking.
for (const para of getParagraphs(doc.package.document)) {
console.log(para.paraId, getParagraphText(para));
}
const out = await repackDocx(doc); // ArrayBuffer
await writeFile("contract-out.docx", Buffer.from(out));The Document wraps a DocxPackage: package.document is the body (content: BlockContent[] of paragraphs, tables, and block SDTs), plus headers, footers, media, numbering, styles, and the rest of the OOXML parts. Text helpers cover the common reads: getBodyText, getParagraphText, getTableText, getBodyWordCount, hasTables, hasImages, and more.
Two ways back to bytes
repackDocx(doc)round-trips against the document's original buffer, carrying every untouched part through verbatim. Use it whenever the document came fromparseDocx. It throws if there is no original buffer.createDocx(doc)builds a fresh package from scratch. Use it for documents you created in code.
serializeDocx(doc) returns the document.xml markup as a string, not a .docx file; reach for it only when you need the raw XML.
Mutating with DocumentAgent
DocumentAgent wraps a Document in a chainable, immutable API. Each call returns a new agent; nothing is mutated in place.
import { DocumentAgent } from "@eigenpal/docx-editor-core/headless";
const agent = await DocumentAgent.fromBuffer(buffer);
console.log(agent.getWordCount(), agent.getPageCount(), agent.getVariables());
const edited = agent
.insertText({ paragraphIndex: 0, offset: 0 }, "CONFIDENTIAL: ")
.applyStyle(0, "Heading1");
const filled = await edited.applyVariables({
customer_name: "Jane Doe",
date: "2026-07-01",
});
const out = await filled.toBuffer(); // ArrayBufferBeyond text: insertTable, insertImage, insertHyperlink, replaceRange, deleteRange, applyFormatting, applyParagraphFormatting, mergeParagraphs, and executeCommands for batched AgentCommand arrays. toBlob() exists for browser contexts.
Creating documents from scratch
import { createDocumentWithText, createDocx } from "@eigenpal/docx-editor-core/headless";
const doc = createDocumentWithText("Generated on the server.");
const out = await createDocx(doc);createEmptyDocument(options?) gives you a blank Document to build up with DocumentAgent or content-control fills.
Templates and variables
For {{variable}}-style templates, the headless surface bundles a docxtemplater-backed pipeline:
import { detectVariables, parseDocx, processTemplate } from "@eigenpal/docx-editor-core/headless";
const doc = await parseDocx(buffer);
console.log(detectVariables(doc)); // ["customer_name", "date"]
const out = processTemplate(buffer, { customer_name: "Acme GmbH", date: "2026-07-01" }); // ArrayBuffervalidateTemplate, getMissingVariables, previewTemplate, and processTemplateDetailed cover validation and error reporting. For structured fills anchored to Word content controls instead of mustache tags, see Content controls; both work headless.
When to use this vs DocxReviewer
- Headless core (this page) is deterministic plumbing: you write the logic, it executes. Use it for template fills, extraction, format conversion steps, and any pipeline where the transformation is known in advance.
DocxReviewerfrom@eigenpal/docx-editor-agentswraps this same engine in an LLM tool surface: an agent reads the document, decides what to change, and applies edits (including tracked changes) through tool calls. Use it when the transformation requires judgment. See DocxReviewer.
The two compose: a pipeline can fill a template headlessly, then hand the buffer to a DocxReviewer pass for AI review.
Next steps
- Core headless API for the full export list
- Content controls for tag-addressed document generation
- AI & Agents for the agent-driven alternative