# word-kit — full documentation This file is the concatenation of every page on the word-kit docs site, intended for LLM ingestion in a single fetch. Page boundaries are marked with H1 headings prefixed by the source path. The companion index at `/llms.txt` lists the same pages with one-line descriptions. Source repo: https://github.com/office-kit/docx --- # word-kit — overview OOXML-compliant (ECMA-376 Part 1 — WordprocessingML) Word `.docx` library for Node 22+ and modern browsers. Function-first API: every operation is a standalone, tree-shakeable export, never a method on a class. Lossless round-trip for every element the library does not yet model, so hand-designed templates survive intact. Two packages on npm: - `@office-kit/docx` — the public authoring API. - `@office-kit/docx-preview` — optional companion that mounts a read-only preview of any `Docx` (or raw bytes) into a DOM container, by wrapping the OSS `docx-preview` renderer. Bundle budgets (CI-enforced): minimal `createDocx + appendParagraph + toUint8Array` slice ~42 KB minified; full surface ~131 KB. Test gate: 512 vitest tests on Node 22 and 24 every change. Real-Word fixture corpora (mammoth.js + python-docx) round-trip-verified. --- # Getting started Install both packages: ```sh pnpm add @office-kit/docx @office-kit/docx-preview ``` Both ship as ESM with bundled `.d.ts` types. Neither has Node-only dependencies. ## Build a document from scratch The hello-world. `createDocx` returns a plain `Docx` object that the rest of the API treats as a value. ```ts title="site/src/lib/examples/from-scratch.ts" // Build a .docx from scratch. This file is imported as ?raw into the // docs site so the snippet shown to readers is exactly what svelte-check // type-checked — if an API rename breaks it, the docs build fails. import { addBulletList, addTable, appendHeading, appendParagraph, createDocx, PAGE_SIZE_A4, setPageSize, toUint8Array, } from "@office-kit/docx"; const doc = createDocx({ paragraphs: [] }); setPageSize(doc, PAGE_SIZE_A4); appendHeading(doc, "Quarterly review", 1); appendParagraph(doc, "Highlights from Q3."); addBulletList(doc, ["Shipped preview", "Round-trip stable", "512 tests green"]); addTable(doc, [ ["Metric", "Q2", "Q3"], ["MRR", "$120k", "$148k"], ["Churn", "3.1%", "2.4%"], ]); const bytes: Uint8Array = toUint8Array(doc); void bytes; ``` ## Open a template, fill placeholders Existing `.docx` files are opened with `openDocx` and edited in place. word-kit preserves every XML element it does not yet model as a pass-through node, so re-saving an unmodified template doesn't trip Word's "needs repair" prompt. `replaceTextEverywhere` walks every story — body, headers, footers, footnotes, endnotes, comments, textboxes — not just the main document. Run-spanning matches like `{{name}}` split across multiple runs are joined before the regex sees them. ```ts title="site/src/lib/examples/template-fill.ts" // Open an existing .docx as a template, run cross-part placeholder // substitution, write it back. The interesting bit: replaceTextEverywhere // also walks headers / footers / footnotes / textboxes — not just the // body — so {{name}} in a header swaps out too. import { openDocx, replaceTextEverywhere, toUint8Array } from "@office-kit/docx"; declare const templateBytes: Uint8Array; declare const values: Record; const doc = openDocx(templateBytes); const replaced: number = replaceTextEverywhere( doc, /\{\{(\w+)\}\}/g, (m) => values[m.captures[0] ?? ""] ?? "", ); const out: Uint8Array = toUint8Array(doc); void replaced; void out; ``` ## Render in the browser The companion package `@office-kit/docx-preview` mounts a read-only preview of any `Docx` into a DOM container. It wraps the OSS `docx-preview` renderer behind a stable function-API entry point. ```ts title="site/src/lib/examples/preview-embed.ts" // Render any Docx — built from scratch or opened from bytes — into a // DOM container using @office-kit/docx-preview. The wrap is intentional: we // share the renderer with docx-preview upstream but pin the contract // behind word-kit's stable function-API surface. import { openDocx } from "@office-kit/docx"; import { previewToDOM } from "@office-kit/docx-preview"; declare const bytes: Uint8Array; declare const container: HTMLElement; const doc = openDocx(bytes); const handle = await previewToDOM(doc, container, { classPrefix: "wk-", inWrapper: true, breakPages: true, renderFonts: true, }); // when you're done, detach + release internal references: handle.dispose(); ``` --- # Recipes Type-checked snippets for common scenarios. Every snippet below lives under `site/src/lib/examples/` and is type-checked by `svelte-check` against the live `@office-kit/docx` / `@office-kit/docx-preview` surface — an API rename breaks the docs build before anything ships. Pointers under each snippet name the matching sample file produced by `pnpm sample` (or the integration test that exercises the same path). ### 01 · Mail-merge across every story in the package replaceTextEverywhere walks body, headers, footers, footnotes, comments, and textboxes — so a placeholder in a header is filled the same way one in the body is. *Where:* `samples/20-mailmerge-text-replace-*.docx (run `pnpm sample`)` ```ts title="site/src/lib/examples/recipe-mail-merge.ts" // Mail-merge across every story in the package — body, headers, // footers, footnotes/endnotes, comments, textboxes. replaceTextEverywhere // follows the same regex/callback contract as replaceText but walks the // whole document, so a {{name}} hidden in a header is filled the same // way one in the body is. import { openDocx, replaceTextEverywhere, toUint8Array } from "@office-kit/docx"; declare const templateBytes: Uint8Array; declare const values: Record; const doc = openDocx(templateBytes); // Regex captures land in `m.captures`; everything else (`m.match`, // `m.index`) mirrors RegExp.exec semantics. Run-spanning matches are // joined before the regex sees them, so `{{name}}` split across // multiple runs still substitutes cleanly. const replaced: number = replaceTextEverywhere( doc, /\{\{(\w+)\}\}/g, (m) => values[m.captures[0] ?? ""] ?? "", ); console.log(`replaced ${replaced} placeholders`); const filled: Uint8Array = toUint8Array(doc); void filled; ``` ### 02 · PowerPoint-style designed base Open a hand-designed .docx, lift its style table into your authoring graph with mergeStylesFromTemplate, then keep appending. Custom styles applied by name via setParagraphStyle. *Where:* `samples/30-styled-base-*.docx` ```ts title="site/src/lib/examples/recipe-styled-base.ts" // PowerPoint-style "designed base": open a hand-designed .docx, lift // only its style table into your authoring graph, then keep appending // body content. The visual identity (fonts, colours, custom paragraph // styles) carries over without any placeholder-substitution dance. import { appendHeading, appendParagraph, createDocx, findStyleIdByName, mergeStylesFromTemplate, setParagraphStyle, toUint8Array, } from "@office-kit/docx"; declare const templateBytes: Uint8Array; const doc = createDocx({ paragraphs: [] }); // Merge styles from the hand-designed template into the empty doc. // `overwrite: false` (default) means built-in heading styles in the // authoring graph win if both define them; pass `overwrite: true` to // take the template's flavour. const merged: number = mergeStylesFromTemplate(doc, templateBytes); console.log(`merged ${merged} styles from template`); appendHeading(doc, "Quarterly review", 1); // Apply a custom paragraph style by *name* (as it appears in Word's UI) // rather than the raw `w:styleId`. Returns the underlying styleId so // downstream code can re-use it. const calloutId: string | undefined = findStyleIdByName(doc, "Callout"); const callout = appendParagraph(doc, "Heads-up: this is the executive summary."); if (calloutId) setParagraphStyle(callout, calloutId); const bytes: Uint8Array = toUint8Array(doc); void bytes; ``` ### 03 · Accept or reject tracked changes in bulk acceptAllRevisions inlines suggested inserts and bakes in deletions. rejectAllRevisions does the inverse — useful for producing both 'final' and 'original' branches from one reviewed doc. *Where:* `samples/09-tracked-changes.docx` ```ts title="site/src/lib/examples/recipe-tracked-changes.ts" // Resolve tracked changes (Word's "Accept / Reject all changes") in // bulk. word-kit walks every w:ins / w:del / w:moveFrom / w:moveTo // marker in the package and either bakes it in or removes it. // // `acceptAllRevisions` inlines the inserted ranges and deletes the // deleted ones. `rejectAllRevisions` does the reverse: keeps deletions // (= drops the suggested insertions) and restores deleted ranges. import { acceptAllRevisions, openDocx, rejectAllRevisions, toUint8Array } from "@office-kit/docx"; declare const reviewedBytes: Uint8Array; // Final draft: accept everything the editor suggested. const accepted = openDocx(reviewedBytes); const acceptedCount: number = acceptAllRevisions(accepted); console.log(`accepted ${acceptedCount} revisions`); const finalBytes: Uint8Array = toUint8Array(accepted); // Sometimes you want the opposite — revert all proposed changes. const reverted = openDocx(reviewedBytes); const rejectedCount: number = rejectAllRevisions(reverted); console.log(`rejected ${rejectedCount} revisions`); const originalBytes: Uint8Array = toUint8Array(reverted); void finalBytes; void originalBytes; ``` ### 04 · Render in the browser with @office-kit/docx-preview previewToDOM mounts a read-only render of any Docx into a DOM container. Returns an idempotent dispose handle. *Where:* `see live in the /playground page` ```ts title="site/src/lib/examples/preview-embed.ts" // Render any Docx — built from scratch or opened from bytes — into a // DOM container using @office-kit/docx-preview. The wrap is intentional: we // share the renderer with docx-preview upstream but pin the contract // behind word-kit's stable function-API surface. import { openDocx } from "@office-kit/docx"; import { previewToDOM } from "@office-kit/docx-preview"; declare const bytes: Uint8Array; declare const container: HTMLElement; const doc = openDocx(bytes); const handle = await previewToDOM(doc, container, { classPrefix: "wk-", inWrapper: true, breakPages: true, renderFonts: true, }); // when you're done, detach + release internal references: handle.dispose(); ``` --- # API reference `@office-kit/docx` exposes 121 standalone functions and constants, grouped by area below. `previewToDOM` lives in the companion `@office-kit/docx-preview` package; everything else lives in `@office-kit/docx`. For full signatures, read `src/index.ts` in the repo. ### 01 · Lifecycle - `createDocx`: `({ paragraphs? }?) => Docx` - `openDocx`: `(bytes: Uint8Array) => Docx` - `fromBlob`: `(blob: Blob) => Promise` - `toUint8Array`: `(doc: Docx) => Uint8Array` - `toBlob`: `(doc: Docx) => Blob` - `clone`: `(doc: Docx) => Docx` ### 02 · Paragraphs & blocks - `appendParagraph` - `insertParagraphAt` - `removeParagraph` - `appendHeading` - `appendPageBreak` - `appendLineBreak` - `appendSectionBreak` - `clearBody` - `paragraphs` ### 03 · Inline & text - `replaceText` - `replaceTextEverywhere` - `findText` - `findTextEverywhere` - `appendTextRun` - `setParagraphText` - `paragraphText` - `setRunFormat` - `clearRunFormat` - `getRunFormat` - `setParagraphAlignment` - `getParagraphAlignment` - `setParagraphIndent` - `setParagraphSpacing` - `setParagraphBorders` - `setParagraphShading` - `getParagraphStyle` - `getParagraphNumbering` - `mergeAdjacentRuns` - `mergeAdjacentRunsInBody` ### 04 · Styles & numbering - `addStyle` - `removeStyle` - `listStyles` - `ensureHeadingStyles` - `findStyleIdByName` - `setParagraphStyle` - `addBulletList` - `addNumberedList` - `applyListToParagraph` - `mergeStylesFromTemplate` ### 05 · Tables - `addTable` - `tables` - `removeTable` - `removeAllTables` - `unwrapTable` - `appendTableRow` - `removeTableRow` - `setTableRowAsHeader` - `setTableRowHeight` - `setTableBorders` - `setTableCellText` - `getTableCellText` - `setTableCellShading` - `setTableCellVerticalAlign` ### 06 · Images - `addImage` - `addImageRun` - `insertImageInto` - `images` - `imageReferences` - `replaceImage` - `replaceImageByAltText` - `removeAllImages` ### 07 · Headers, footers, sections - `addHeader` - `addFooter` - `addPageNumberFooter` - `setPageSize` - `setPageMargins` - `setPageOrientation` - `headers` - `footers` - `removeAllHeaders` - `removeAllFooters` ### 08 · Comments, notes, hyperlinks, bookmarks - `addComment` - `addFootnote` - `addEndnote` - `removeAllComments` - `removeAllFootnotes` - `removeAllEndnotes` - `addHyperlink` - `addInternalHyperlink` - `externalHyperlinks` - `setHyperlinkUrl` - `removeAllHyperlinks` - `addBookmark` - `removeBookmark` - `removeAllBookmarks` - `bookmarks` ### 09 · Fields & tracked changes - `appendField` - `addTableOfContents` - `appendMergeField` - `acceptAllRevisions` - `rejectAllRevisions` ### 10 · Document properties - `coreProperties` - `setCoreProperties` - `appProperties` - `setAppProperties` - `title` - `author` - `setTitle` - `setAuthor` ### 11 · Diagnostics - `validate` - `validatePackage` - `statistics` - `outline` - `fields` - `text` ### 12 · Low-level part access - `stylesPart` - `numberingPart` - `commentsPart` - `footnotesPart` - `endnotesPart` ### 13 · Page-size & margin constants - `PAGE_SIZE_A4` - `PAGE_SIZE_LETTER` - `MARGINS_NORMAL` - `VERSION` ### 14 · Browser preview (@office-kit/docx-preview) - `previewToDOM`: `(source, container, options?) => Promise` --- # Playground An interactive page at `/playground`. Drop a `.docx` onto the canvas (or click "Generate sample" to build one in the browser via `@office-kit/docx`) and see it rendered live by `@office-kit/docx-preview`. The bytes never leave the browser. The "Download current bytes" button hands back whatever document is mounted right now — useful for shipping an edited workbook back out of the page after a transform. --- # Scope ## In scope WordprocessingML (`.docx`) — read, edit, write. OPC packaging (ECMA-376 Part 2) and DrawingML are part of the OOXML stack and live in their own packages, but exist to serve docx. Browser preview lives in the companion `@office-kit/docx-preview`. ## Out of scope (for now) `.pptx` (PresentationML) and `.xlsx` (SpreadsheetML). For spreadsheets, see the sibling project [xlsx-kit](https://github.com/baseballyama/xlsx-kit). ## Out of scope (permanent) Rendering to PDF, headless Word automation, binary `.doc` (pre-2007 Word).