§ 01 · Getting started

Install, build a document, write the bytes.

word-kit is a function-first WordprocessingML library. Every operation is a standalone export that takes a Docx value as its first argument; nothing is hidden behind a class instance. That keeps the public surface tree-shakeable and makes the library work the same way in Node and in browsers.

Install

$ pnpm add @office-kit/docx @office-kit/docx-preview

@office-kit/docx is the authoring API. @office-kit/docx-preview is an optional companion that mounts a read-only preview of any Docx value into a DOM container. Both ship as ESM with bundled .d.ts types and have no Node-only dependencies.

Build a document from scratch

The "hello world" of word-kit. Every helper here lives on @office-kit/docx; createDocx hands back a plain Docx object that the rest of the API treats as a value.

A1 site/src/lib/examples/from-scratch.ts .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 can be 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.

A2 site/src/lib/examples/template-fill.ts .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<string, string>;

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 (or raw bytes) into a DOM container. It wraps the OSS docx-preview renderer behind a stable function-API entry point.

You can play with it on the playground, or read the recipes for embedding patterns.

What's next