@office-kit/docx
v0.1.1
Published
OOXML-compliant .docx generation for browser and Node.js.
Maintainers
Readme
@office-kit/docx
OOXML-compliant (ECMA-376) .docx generation library. Runs in modern
browsers and Node.js.
- Build a Word document from scratch with a programmatic API.
- Open an existing
.docxas a template, edit it, and serialize it back. - Target: full WordprocessingML coverage of ECMA-376 Part 1.
Status: pre-1.0. Core functionality (
openDocx/createDocx/toUint8Array) is stable; details may shift before v1. SeeCLAUDE.mdfor engineering principles and scope rules andPLAN.mdfor the milestone roadmap.
Install
pnpm add @office-kit/docx
# or: npm install @office-kit/docx / yarn add @office-kit/docx@office-kit/docx ships ESM-only with bundled .d.ts types. It has no
Node-only dependencies and works in browsers (including with the Blob and
File APIs).
Quick start
import {
addBulletList,
addTable,
appendParagraph,
createDocx,
openDocx,
PAGE_SIZE_A4,
replaceTextEverywhere,
setPageSize,
toUint8Array,
} from "@office-kit/docx";
// From scratch
const doc = createDocx({ paragraphs: ["Hello, world."] });
appendParagraph(doc, "Bullets:");
addBulletList(doc, ["one", "two", "three"]);
addTable(doc, [
["Name", "Score"],
["Alice", "90"],
]);
setPageSize(doc, PAGE_SIZE_A4);
const bytes = toUint8Array(doc);
// From an existing .docx template
const tpl = openDocx(existingDocxBytes);
replaceTextEverywhere(tpl, /\{\{(\w+)\}\}/g, (m) => values[m.captures[0]!] ?? "");
const out = toUint8Array(tpl);Why standalone functions instead of methods? Each operation is its own export, so a bundler can tree-shake any function you don't import. A minimal
createDocx + appendParagraph + toUint8Arrayslice bundles to ~42 KB minified; the full surface is ~131 KB. CI enforces both numbers.
See docs/examples.md for the full API
walkthrough (images, comments, footnotes, headers/footers, bookmarks,
hyperlinks, tracked changes, core document properties).
Browser preview
Pair @office-kit/docx with @office-kit/docx-preview
to render any Docx value as a read-only DOM tree:
import { openDocx } from "@office-kit/docx";
import { previewToDOM } from "@office-kit/docx-preview";
const doc = openDocx(bytes);
const handle = await previewToDOM(doc, document.getElementById("preview")!);
// later:
handle.dispose();@office-kit/docx-preview wraps the OSS docx-preview
renderer behind a stable function-API entry point. The wrap is intentional and
final — see docs/PLAN-PREVIEW.md for the rationale.
Scope
In scope
- WordprocessingML (
.docx) — read, edit, write. - OPC packaging (ECMA-376 Part 2), DrawingML — as the underlying layers.
- Browser preview —
@office-kit/docx-previewwrapsdocx-previewso callers can render docx content into a DOM container without spinning up a server.
Out of scope (for now)
.pptx(PresentationML) and.xlsx(SpreadsheetML).
Out of scope (permanent)
- Rendering to PDF, headless Word automation, binary
.doc(pre-2007).
If a feature request only makes sense for pptx / xlsx, it will be redirected to
a more appropriate library. See CLAUDE.md ("Scope discipline").
Repository layout
.
├── src/ # @office-kit/docx — the published library
│ ├── index.ts # public entry (re-exports src/api)
│ ├── api/ # public Docx wrapper (docx / validator / version)
│ └── internal/ # bundled layers — not separate npm packages
│ ├── opc/ # OPC (ZIP + Content Types + rels)
│ ├── xml/ # namespace-aware XML parser/serializer
│ └── wordprocessingml/ # WML AST + parsers + builders
├── packages/
│ └── preview/ # @office-kit/docx-preview — browser preview (wraps docx-preview)
├── site/ # SvelteKit docs site (pagefind search + preview playground)
├── docs/
│ ├── specs/ # Distilled spec notes + ECMA-376 fetcher target
│ └── PLAN-PREVIEW.md # Why @office-kit/docx-preview wraps docx-preview, final
├── references/ # External OSS / spec material (submodules)
├── scripts/
│ ├── fetch-specs.sh # Downloads ECMA-376 PDFs + XSDs into docs/specs/
│ ├── generate-samples.mjs # `pnpm sample` — writes 32 demonstration .docx files
│ └── check-tree-shake.mjs # `pnpm check:tree-shake` — bundle-budget CI gate
├── .changeset/ # Changesets — drives versioning + npm release
├── .github/ # Issue / PR templates and CI / release / deploy workflows
├── .claude/skills/ # Workflow guides for Claude Code agents
├── CLAUDE.md # Engineering principles for contributors and AI
├── CONTRIBUTING.md # Dev environment, workflow, code style
├── PLAN.md # Living implementation plan + progress table
├── SECURITY.md # Private vulnerability reporting
├── .oxlintrc.json # oxlint config
└── .oxfmtrc.json # oxfmt config (Prettier-compatible)Development
pnpm install # one-time setup (use --recurse-submodules for fixture corpora)
pnpm -r run typecheck # tsc --noEmit (library + preview)
pnpm lint # oxlint
pnpm format:check # oxfmt --check
pnpm build # tsdown (rolldown) — builds the @office-kit/docx library
pnpm build:all # build every workspace package (library, preview, site)
pnpm test # vitest (source-resolved, no build step needed)
pnpm check:tree-shake # CI bundle-budget gate (~42 KB minimal vs ~131 KB full)
pnpm sample # writes demonstration .docx files into ./samples/For the docs site:
pnpm --filter word-kit-site dev # http://localhost:5173 with hot reload
pnpm --filter word-kit-site build # static export + pagefind search indexSee .claude/skills/run-check-and-test/SKILL.md
for the canonical pre-PR quality gate.
Contributing
- Read
CONTRIBUTING.mdfor the development environment, workflow, and test layout. - Issues and PRs must follow the templates under
.github/. Submissions that strip the template structure are auto-closed by the template-compliance workflow. - Read
CLAUDE.mdbefore opening a PR — it covers the "one way to do one thing" rule, defensive programming, comments, and the hard "no"s (noas unknown as T, no N+1, etc.).
