dom-docx
Convert semantic HTML fragments to native, editable Word documents (OOXML): paragraphs, runs, lists, tables, images. Not screenshots or layout hacks.
Live demo: dom-docx.com. Try the converter, browse showcases, read the learn guide.
Built with a visual regression loop: render HTML in Chromium, convert to docx, rasterize via LibreOffice, score layout + structural fidelity against a human-validated metric, iterate. Latest scores: TEST-SCORES.md · methodology: SCORING.md.
Install
Requires Node.js ≥ 20. No browser or Playwright is needed for the default inline path.
When is Playwright needed?
On Node, playwright is an optional peer dependency. npm install dom-docx pulls only docx, cheerio and fflate, nothing heavy. It is loaded lazily when you pass styleSource: "computed" or rasterizeInPlace. To use those paths, install Playwright and Chromium yourself, once:
Playwright is also used by the dev test harness (not required to use the library). Contributors: npm run setup after clone.
LibreOffice is not needed to convert. It is only used for the visual test harness.
CLI
Convert a file without writing any code:
Input is a body HTML fragment, same as the API. --help for all options.
Quick start
Browser
No Playwright, no Node. This runs entirely in the user's tab. See Browser bundle below.
Node
Pass a body fragment only (no <!DOCTYPE> / <html> / <body> required). Defaults: US Letter, 1″ margins, Arial 10.5pt body text.
v0.1.x capability
Supported (default styleSource: "inline"):
Headings, paragraphs, lists (<ul>/<ol> including list-style-type), tables, links, inline formatting
Block backgrounds, blockquotes, <hr>, simple flex rows (≤4 items)
data: images; remote images via your imageResolver
Page size/orientation/margins, default font, metadata, header/footer HTML, page numbers, table of contents, lang/direction
Low-complexity inline SVG (bars + text)
CSS bar divs in table cells (background + height/width → native shaded bands)
Advanced (optional styleSource: "computed"):
Resolves <style> blocks and class/#id selectors via getComputedStyle
Node: requires playwright (optional peer dependency, installed separately) + Chromium. The library launches headless Chromium to render the fragment
Browser bundle: uses the live DOM in the user's tab. No Playwright, no extra install
SPA fragment export: pass root (browser) or rootSelector (Node + live page) when converting element.innerHTML so computed-style paths match the fragment tree
Inline is the supported default for npm installs; computed is for stylesheets/classes or when you already have a rendered page
Charts & complex SVG (optional rasterizeInPlace):
Rasterizes <canvas> and complex <svg> (e.g. Highcharts) to PNG <img> before conversion
Recommended for charts: rasterizeInPlace: { scale: 2 } — supersamples at 2× density for sharper images in Word (default scale: 1; max 4)
Browser: requires root. Clones off-screen by default so the live page is not mutated
Node: uses the same Playwright/Chromium context as computed styles; ephemeral spawn pages mutate in place by default
Simple inline SVG (rect + text bar charts) still converts natively without rasterization
Not supported in v0.1.x:
External stylesheets on the inline path (use computed or inline all styles)
Web fonts, CSS grid/float layout, forms, <pre> polish, <dl>, table rowspan
Header/footer first/even page variants; guaranteed multi-page layout fidelity
Complex SVG (paths, gradients, <use>) unless rasterizeInPlace is used on a live rendered page
See AGENTS.md for HTML authoring tiers and API.md for full options.
API
convertHtmlToDocx(html, options?)
Returns Promise<Buffer> (Node) with a valid .docx file.
Options
Images
Only data: URLs embed automatically. For http(s): or file paths, supply a resolver. You control fetch policy and security:
Browser bundle
For client-side conversion (returns a Blob). No Playwright. The bundle runs entirely in the user's browser.
Browser-only options: root, document, rasterizeInPlace. Build: npm run build:browser → dist/browser/dom-docx.browser.js.
For advanced usage (buildDocxBuffer, custom StyleResolver, engine architecture), see API.md.
What converts well
Optimized for Word-friendly semantic HTML: headings, paragraphs, lists, data tables, inline formatting, shaded callouts, simple flex rows.
Full authoring guide for agents: AGENTS.md.
How it was built
dom-docx maps a practical HTML subset to native OOXML through a three-stage pipeline:
Style resolution: inline style="" (default) or browser computed styles
Visitor: Cheerio walk → docx paragraphs, tables, numbering, hyperlinks
Pack + patch: generate OOXML, patch list numbering and shaded-block alignment for LibreOffice/Word PDF export
Quality is driven by an autonomous loop rather than one-off visual checks:
30+ regression cases (defined in tools/generator.ts, run via npm run score:suite): human-validated layout fidelity (ink-projection structure comparison, 85.6% concordance with blind human quality ratings), plus guards for bad contrast, missing list markers, wrong text and imbalanced shaded blocks; raw pixel match is recorded as a regression tripwire
Engine score: 50% visual (layout-based) + 35% editability (native structure, not 1×1 layout tables) + 15% compile speed
OSS benchmark: same harness scores html-to-docx and @turbodocx/html-to-docx for ongoing comparison (BENCHMARK.md)
The default inline path is pure JavaScript (docx + cheerio + fflate) with no browser required. Playwright is Node-only: for styleSource: "computed" on the server and for the dev harness. The dom-docx/browser bundle never uses Playwright; computed styles come from the live page's getComputedStyle.
Full scoring formulas, subscores, calibration and the agent iteration workflow: SCORING.md.
Development
For contributors and harness runs (not required to use the library):
Prerequisites for the harness: LibreOffice (soffice) for PDF rasterization, Playwright Chromium (npm run setup).
Documentation
License
MIT