A client sent me a contract draft as a .docx file. I needed to turn it into a PDF and send it back. I did not want to open Microsoft Word. I did not want to upload the file to a server.
So I built client-side Word to PDF conversion into my Vue 3 PDF toolkit.
Why Client-Side?
Most online Word-to-PDF converters work like this:
- Upload your .docx file to their server
- The server converts it with LibreOffice or similar
- You download the PDF
The problem is obvious: your document sits on someone else's computer. For contracts, resumes, and reports, that is not ideal.
Client-side conversion keeps the file in the browser. The trade-off is that you have to handle .docx parsing, HTML rendering, and PDF generation yourself.
The Stack
- Vue 3 — UI and state management
- mammoth — Parse .docx files into clean HTML
- html2pdf.js — Convert the rendered HTML into a PDF
npm install mammoth html2pdf.js
Parsing the .docx File
Mammoth converts the Word document into semantic HTML, ignoring most of the visual styling but preserving structure.
import mammoth from 'mammoth'
async function parseDocx(file: File) {
const arrayBuffer = await file.arrayBuffer()
const result = await mammoth.convertToHtml({ arrayBuffer })
return result.value // HTML string
}
The result is HTML with tags like <p>, <h1>, <table>, <ul>, and <img>.
Rendering a Preview
I render the HTML in a hidden or visible preview container so the user can check the conversion before downloading.
<template>
<div ref="preview" class="docx-preview" v-html="htmlContent" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const preview = ref<HTMLElement>()
const htmlContent = ref('')
</script>
I apply a print-friendly CSS reset to the preview so headings, tables, and lists look reasonable.
.docx-preview {
font-family: Georgia, serif;
line-height: 1.6;
max-width: 210mm;
margin: 0 auto;
}
.docx-preview h1, .docx-preview h2, .docx-preview h3 {
margin-top: 1.5em;
margin-bottom: 0.5em;
}
.docx-preview table {
width: 100%;
border-collapse: collapse;
}
.docx-preview td, .docx-preview th {
border: 1px solid #ddd;
padding: 8px;
}
Generating the PDF
Once the user confirms the preview looks right, I use html2pdf.js to render the preview element to a PDF.
import html2pdf from 'html2pdf.js'
function downloadPdf() {
const element = preview.value
if (!element) return
const opt = {
margin: 10,
filename: 'converted.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
}
html2pdf().set(opt).from(element).save()
}
The scale: 2 setting gives crisp text and images. The A4 portrait format matches most document expectations.
Handling Images
.docx files often contain embedded images. Mammoth converts these to base64 data URLs by default, which work fine in both the preview and the PDF output.
For very large images, I recommend adding a CSS rule to constrain the width:
.docx-preview img {
max-width: 100%;
height: auto;
}
Limitations
This approach works well for text-heavy documents with basic formatting. It does not perfectly preserve:
- Complex page layouts
- Advanced Word styles and themes
- Embedded fonts that are not web-safe
- Form fields and macros
- Tracked changes and comments
For those cases, a desktop editor is still the better choice.
Try It
You can see the final tool here: en.sotool.top/word-to-pdf
Need advanced document editing, OCR, or precise PDF control? Wondershare PDFelement is a desktop alternative worth considering. This post contains affiliate links.












