§ 02 · Recipes

Common scenarios, type-checked snippets.

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 this page 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`)

A1 site/src/lib/examples/recipe-mail-merge.ts .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<string, string>;

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

B1 site/src/lib/examples/recipe-styled-base.ts .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

C1 site/src/lib/examples/recipe-tracked-changes.ts .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

D1 site/src/lib/examples/preview-embed.ts .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();

Want to drive a doc end-to-end? Walk the Getting started page, then poke at the playground to see the rendered output.