How to Convert Word to PDF in the Browser with Vue 3, mammoth, and html2pdf.js

3
calendar_today agoschedule4 min read
— Originally published at dev.to

Converting Word documents to PDFs on the server is the classic approach: upload the file, run LibreOffice or a cloud API, send the result back. But that means your users’ resumes, contracts, and reports touch your infrastructure.

I wanted something simpler for en.sotool.top: pick a .docx file in the browser, preview the parsed content, and download a PDF. No server involved.

Here is how I built it with Vue 3, mammoth, and html2pdf.js.


Why Client-Side?

The main reason is privacy. Resumes, contracts, tax documents — users do not want them on a stranger’s server. Client-side conversion also means:

  • No upload bandwidth limits
  • No file size caps from your server
  • No storage to clean up
  • Works offline after the page loads

The trade-off is that very complex documents are limited by the browser’s rendering capabilities. For typical office documents, that is fine.


The Stack

  • Vue 3 — UI, file handling, and reactive state
  • mammoth — Parse .docx files into clean HTML
  • html2pdf.js — Render the HTML into a PDF using html2canvas + jsPDF
  • Native File API — File selection
npm install mammoth html2pdf.js

Loading the Word Document

The first step is reading the uploaded .docx file into an ArrayBuffer, then converting it to HTML with mammoth.

import mammoth from 'mammoth';

async function convertDocxToHtml(file) {
  const arrayBuffer = await file.arrayBuffer();
  const result = await mammoth.convertToHtml({ arrayBuffer });
  return result.value;
}

mammoth intentionally produces simple, clean HTML. It ignores complex formatting like text boxes and embedded fonts, which makes the output predictable.

I keep the HTML in a reactive ref and render it in a preview panel:

<template>
  <div ref="previewRef" class="word-preview" v-html="htmlContent"></div>
</template>

<script setup>
import { ref } from 'vue';

const htmlContent = ref('');
const previewRef = ref(null);
</script>

Generating the PDF

Once the user is happy with the preview, html2pdf.js turns the preview element into a PDF. It uses html2canvas to rasterize the HTML and jsPDF to create the PDF file.

import html2pdf from 'html2pdf.js';

async function convertToPdf() {
  const element = previewRef.value;
  if (!element) return;

  await html2pdf()
    .from(element)
    .set({
      margin: [10, 10, 10, 10],
      filename: 'document.pdf',
      image: { type: 'jpeg', quality: 0.98 },
      html2canvas: { scale: 2, useCORS: true },
      jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
    })
    .save();
}

The scale: 2 setting gives crisp text and images on high-DPI screens. useCORS: true helps if the preview contains external images.


Styling the Preview

html2pdf.js renders whatever is in the DOM, so the preview styles directly affect the output. I use scoped CSS with :deep() to style the converted HTML:

.word-preview :deep(h1) {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 1rem;
}

.word-preview :deep(p) {
  margin-bottom: 0.75rem;
  line-height: 1.7;
}

.word-preview :deep(table) {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1rem;
}

.word-preview :deep(td),
.word-preview :deep(th) {
  border: 1px solid #d1d5db;
  padding: 0.5rem;
}

Keep the preview background white. Dark mode UI is nice for the app, but a white page produces a clean PDF.


Handling Errors Gracefully

Not every file is a valid .docx. I wrap the conversion in a try/catch and reset the state on failure:

async function handleFile(file) {
  try {
    const arrayBuffer = await file.arrayBuffer();
    const result = await mammoth.convertToHtml({ arrayBuffer });
    htmlContent.value = result.value;
  } catch (e) {
    console.error(e);
    alert('Failed to parse the document. Please check the file format.');
  }
}

You can also surface mammoth warnings if you want to show the user which styles or elements were ignored.


Lessons Learned

1. Mammoth is opinionated by design
It strips complex Word features to produce clean HTML. If your users need pixel-perfect fidelity, a server-side converter is still a better fit.

2. Page size matters
If the document contains wide tables or landscape sections, A4 portrait may clip them. Consider letting users choose the output format.

3. html2canvas is heavy on large documents
Very long documents with many images can take several seconds and block the main thread. For heavy usage, consider chunking or using a Web Worker.

4. Keep the original file around
If users want to adjust settings and re-convert, you will need the original File object or ArrayBuffer because mammoth does not expose an editable model.


Try It

The Word to PDF tool is live at en.sotool.top/word-to-pdf.

Free, no signup, and nothing uploads to a server.

Full source code is on GitHub. The conversion logic lives in src/views/WordToPdf.vue.


Want More Advanced PDF Features?

If you need OCR, form editing, digital signatures, or batch processing beyond what a browser tool can do, Wondershare PDFelement is a solid desktop option. It keeps everything local and adds professional tools on top.

This post contains affiliate links.


Have you built client-side document converters? What did you use — mammoth, pdf-lib, or a server-side tool?

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Local-First: The Browser as the Vault

Pocket Portfolio - Apr 20

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23
chevron_left
146 Points3 Badges
2Posts
0Comments
1Connections
Independent developer building free, browser-based PDF tools. No uploads, no servers — your files st... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!