Lossless round-trip
Every XML element word-kit does not yet model is preserved as a pass-through node in its original position. Opening and re-saving an unmodified template leaves Word's "needs repair" prompt out of the picture.
.docx from Node and the browser.WordprocessingML — paragraphs, runs, tables, styles, numbering, headers / footers, sections, images, comments, fields, tracked changes — modeled, edited, and serialized. Lossless round-trip for every part the library does not yet structure, so hand-designed templates survive intact.
pnpm add @office-kit/docx @office-kit/docx-preview Every XML element word-kit does not yet model is preserved as a pass-through node in its original position. Opening and re-saving an unmodified template leaves Word's "needs repair" prompt out of the picture.
Open a hand-designed .docx, merge its styles into your authoring graph with mergeStylesFromTemplate, then keep appending. No {{placeholder}} dance required for the layout itself.
@office-kit/docx-preview wraps the OSS docx-preview renderer behind a single function: previewToDOM(source, container). Drop any Docx, Uint8Array, Blob, or ArrayBuffer into a DOM node.
Every operation is a standalone export. A minimal createDocx + appendParagraph + toUint8Array slice bundles to ~42 KB minified; the full surface is ~131 KB. CI enforces both.
Both files below live under site/src/lib/examples/ and are type-checked by svelte-check against the real library on every build — if an API renames,
the docs build fails.
Heading, body paragraph, bullet list, table, A4 page size — the canonical authoring slice.
// 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;
previewToDOM mounts a read-only render of any Docx into a DOM container. Returns an idempotent dispose handle.
// 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();
More in Getting started, Recipes, and the browser preview playground.