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-core

Parse, 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 from parseDocx. 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(); // ArrayBuffer

Beyond 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" }); // ArrayBuffer

validateTemplate, 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.
  • DocxReviewer from @eigenpal/docx-editor-agents wraps 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

On this page