Rendering PDF pages as images in the browser sounds like a server-side job, but it is not. With PDF.js and a <canvas> element, you can convert each page to a PNG or JPEG entirely on the client side. No upload, no backend processing, and full control over resolution.
This post walks through a minimal but production-ready implementation you can drop into a Vue 3 project.
Why client-side?
Traditional PDF-to-image services upload your file, render it on a server, and send back images. That works, but it introduces latency, bandwidth costs, and privacy risk. A browser-based converter:
- Keeps files on the user's device
- Works offline after the app loads
- Avoids server-side rendering entirely
The stack
- Vue 3 with Composition API
-
PDF.js (
pdfjs-dist) for rendering - HTML5 Canvas for image generation
- JSZip for packaging multiple images
- Vite for bundling
Minimal implementation
<script setup lang="ts">
import { ref } from 'vue'
import * as pdfjs from 'pdfjs-dist'
import JSZip from 'jszip'
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js'
const file = ref<File | null>(null)
const converting = ref(false)
const images = ref<string[]>([])
const format = ref<'png' | 'jpeg'>('png')
const dpi = ref(150)
async function handleFileUpload(selected: File) {
file.value = selected
}
async function convertToImages() {
if (!file.value) return
converting.value = true
images.value = []
try {
const arrayBuffer = await file.value.arrayBuffer()
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum)
const viewport = page.getViewport({ scale: dpi.value / 72 })
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (!ctx) continue
canvas.width = viewport.width
canvas.height = viewport.height
await page.render({ canvasContext: ctx, viewport }).promise
const dataUrl = canvas.toDataURL(
format.value === 'png' ? 'image/png' : 'image/jpeg',
0.92
)
images.value.push(dataUrl)
}
} finally {
converting.value = false
}
}
async function downloadZip() {
const zip = new JSZip()
images.value.forEach((dataUrl, i) => {
const base64 = dataUrl.split(',')[1]
zip.file(`page-${i + 1}.${format.value}`, base64, { base64: true })
})
const blob = await zip.generateAsync({ type: 'blob' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'pdf-pages.zip'
a.click()
URL.revokeObjectURL(url)
}
</script>
The key is page.getViewport({ scale: dpi.value / 72 }). PDF.js uses 72 DPI as its base scale, so dividing your target DPI by 72 gives the correct canvas size for the resolution you want.
Handling large files
Browser memory is the main constraint. For a production tool, add two limits:
const MAX_SIZE = 100 * 1024 * 1024 // 100 MB
const MAX_PAGES = 200
Validate before loading into PDF.js. High-DPI rendering of large pages can produce very large canvases, so you may also want to cap the maximum pixel dimensions.
UX tips from a live tool
At en.sotool.top/pdf-to-image, we learned a few things from real users:
- Let users choose format and DPI. Some want PNG transparency, others want small JPEGs. Some need 300 DPI for print.
- Show page previews. Users want to confirm the conversion looks right before downloading.
- Offer a ZIP download. Individual image downloads are tedious for multi-page documents.
- Separate "completed" from "downloaded." Track both events separately so you can measure drop-off between processing success and the actual save.
Tracking the funnel
We use GA4 custom events:
onFileUpload(file)
onActionClick('convert-to-image')
onCompleted({ output_format: format.value, dpi: dpi.value })
onDownload({ file_count: images.value.length })
This lets us see exactly where users drop off: upload, action click, completion, or download.
Going further
For simple PDF-to-image conversion, PDF.js plus Canvas is enough. If you need OCR, batch automation, or direct editable text export, you'll want a server-side component or a desktop tool.
Want to see the full source? The site is built in public at github.com/sunshey/pdf-tool (replace with your actual repo if different).
If you need desktop-grade PDF editing — OCR, batch conversion, or advanced export formats — check out Wondershare PDFelement.
Top comments (0)