Engineering Journal
Pdf Processor
Pdf Processor

Installing a conversion library is a habit, not a decision

2026-08-05

TLDR

We needed DOCX import in a browser-based document tool, and our first instinct was to install a conversion library. The tool only needs a slice of DOCX, and it needs that slice mapped into its own schema anyway. We parsed the format with primitives the browser already ships, a zip scan, a decompression stream, and an XML parser, and added import support with zero new dependencies. The build stayed at 4,446 kB and no converter library entered our package.json. The library is worth it when the format surface is genuinely huge and central, not when you need paragraphs, lists, and tables.
ApproachDependenciesCoverageWhen it wins
Converter library+1 heavy dependencyFull format surfaceFull Word fidelity, exotic layouts
Platform-primitive subset parserZero new depsThe slice your schema needsOne format, controlled contract

The position

Default to parsing the single format you need with the primitives the platform already gives you. Install a converter only after you can name the features it saves you. Not before.

What the industry does and why

Most teams reach for a library. Office Open XML looks intimidating, a zip of XML namespaces, and battle-tested libraries exist, so the decision feels free.

It is not free. You pay in bundle size, in an API you must learn, and in a conversion you still must write, because the library returns its own document model and your tool has its own.

Why it fails for this problem class

We do not need DOCX. We need the slice of DOCX that our schema describes: paragraphs, styled runs, ordered lists, tables with headers, page width.

A converter library models all of OOXML: themes, footnotes, drawingML, tracked changes. We would use maybe ten percent of it. And because the tool owns a typed intermediate representation, the library's output is an intermediate step we would throw away, because we would map its model into ours. The dependency buys a model we do not keep.

In a browser-first tool, a few hundred kilobytes of dependency for one format is a poor trade when the alternative is a few hundred lines of primitives:

const entries = scanZip(file);                // find word/document.xml
const xml = await inflate(entries.contents);  // decompress
const dom = new DOMParser().parseFromString(xml, 'application/xml');
for (const p of dom.getElementsByTagName('w:p')) {
    const runs = [...p.getElementsByTagName('w:r')].map(readRun);
    blocks.push({ type: 'paragraph', runs, align: readAlign(p) });
}

The parser reads only what the schema declares. Unsupported constructs are ignored, not mangled, and the tool decides what "supported" means. Our docx importer ended up 313 lines, dependency-free, and the build output was unchanged:

โœ“ built in 26.28s
main-*.js   4,446.02 kB โ”‚ gzip: 1,194.18 kB

What you give up

Real Word documents are messy. Full-fidelity import, comments, complex numbering, embedded objects, exact styles, requires the library. A subset parser degrades gracefully on those documents. That is the honest trade. You control a smaller, cleaner contract instead of inheriting a huge one.

When the common pattern is right

A converter library is the right call when the format is the product. Full Word round-trip, Excel in, PDF out. Or when you need cross-version fidelity that a battle-tested implementation has already absorbed. Those are the cases where the ten percent you use is still more than you want to own.

Rule of thumb: For each format you import, ask what your schema actually needs from it. If the answer is a subset you can express in a few hundred lines of platform primitives, the library is a habit, not a requirement.
Read this post in the full Engineering Journal โ†’