I Kept Rebuilding PDF Features in Vue 3, So I Built vue3-pdf-export

I Kept Rebuilding PDF Features in Vue 3, So I Built vue3-pdf-export

1 9
calendar_today agoschedule7 min read
— Originally published at dev.to

vue3-pdf-export logo

I Kept Rebuilding PDF Features in Vue 3, So I Built vue3-pdf-export

I recently published vue3-pdf-export, a Vue 3 + TypeScript package for generating PDFs directly from HTML in browser applications.

Vue 3 + TypeScript browser PDF generation with Canvas and selectable-text
rendering, pagination, preview, large-document support, cancellation,
progress reporting, headers, footers, watermarks, security, metadata,
compression, links, and more.

Why I built it

Before publishing vue3-pdf-export, I had already worked on PDF generation in different projects, including Google projects, CRM applications, and other Vue-based applications.

I tried different HTML-to-PDF approaches and existing libraries, but in real projects I often ended up building my own custom Vue PDF component around them to handle the specific requirements I needed.

Whenever a new project needed PDF generation, I found myself solving many of the same problems again:

  • pagination
  • page breaks
  • preview
  • download
  • printing
  • headers and footers
  • image handling
  • different PDF options
  • loading states
  • long-running generation
  • browser memory usage
  • security and metadata

This time, instead of creating another project-specific component, I decided to build a cleaner and more complete solution that I could reuse across Vue 3 projects.

That became vue3-pdf-export.

The main reason was simple: I did not want to keep rebuilding the same PDF functionality again and again.

And if it can also save time for other Vue developers who need similar PDF features, even better.

Installation

npm install vue3-pdf-export

Basic usage

<script setup lang="ts">
import { ref } from 'vue'
import { Html2Pdf } from 'vue3-pdf-export'
import 'vue3-pdf-export/style.css'

const pdf = ref<InstanceType<typeof Html2Pdf> | null>(null)

async function generate() {
  await pdf.value?.generatePdf()
}
</script>

<template>
  <button @click="generate">
    Generate PDF
  </button>

  <Html2Pdf
    ref="pdf"
    filename="example.pdf"
    :preview-modal="true"
  >
    <template #pdf-content>
      <main>
        <h1>Hello from Vue</h1>
        <p>This content will be exported to PDF.</p>
      </main>
    </template>
  </Html2Pdf>
</template>

Two rendering modes

One of the biggest additions is support for two different PDF rendering strategies.

Canvas mode

Canvas mode is the traditional HTML-to-PDF path and is best when browser CSS fidelity is the main priority.

It uses the familiar html2pdf.js / html2canvas / jsPDF pipeline underneath.

<Html2Pdf render-mode="canvas">

This remains the package default for backward compatibility.

Selectable text mode

Text mode generates real PDF text instead of rasterizing the body into a canvas.

<Html2Pdf render-mode="text">

The resulting text can be:

  • selected
  • searched
  • copied

This can be especially useful for reports, invoices, documentation, and other text-heavy PDFs.

Because this renderer does not behave exactly like a browser layout engine, some advanced CSS may render differently compared with Canvas mode.

The idea is to let developers choose between maximum browser-style fidelity and real selectable PDF text depending on their use case.

Pagination

The package supports both automatic and manual pagination.

For an explicit manual page break:

<div class="html2pdf__page-break"></div>

There are also options for keeping elements together and avoiding unwanted page breaks.

Large-document rendering

Large PDFs were one of the areas I wanted to improve significantly.

Canvas rendering can become expensive when a browser attempts to create one extremely tall canvas for an entire document.

vue3-pdf-export can now render paginated Canvas documents incrementally, page by page, instead of depending on one giant full-document capture.

In one benchmark fixture, the previous long-document capture surface was around:

99.96 million pixels

while incremental rendering kept the maximum capture surface around:

1.74 million pixels per page

That is approximately a 98.26% reduction in peak capture-surface geometry for that fixture.

This is specifically a rendering-surface measurement, not a claim that total browser memory always drops by the same percentage.

The current regression suite includes documents across:

10 / 25 / 40 / 50 / 60 / 75 / 100 pages

50-page and 100-page Canvas documents are part of the current verified test range.

100 pages is not a hard package limit. Practical limits still depend on the document, images, browser memory, rendering mode, security settings, and the device being used.

Cancellation

Long-running PDF generation can now be cancelled.

pdf.value?.cancelGeneration()

Cancellation is cooperative and works across the generation pipeline.

The library also tests recovery after cancellation, so a new PDF can be generated after an earlier operation was cancelled.

Some already-running synchronous browser operations cannot always be stopped immediately, but the pipeline checks for cancellation between major stages.

Page-aware progress

Progress reporting now includes more than a simple percentage.

The package can expose:

  • current progress
  • current page
  • total pages
  • current processing stage

Stages include:

pagination
images
preparing
rendering
watermark
header
footer
metadata
serializing
complete
error

This makes it possible to build UI such as:

Rendering page 18 of 50...

instead of showing an indefinite spinner.

Headers

Headers can contain:

  • plain text
  • trusted HTML
  • optional logos
  • custom backgrounds and text colors
  • configurable logo dimensions
  • selected-page targeting

A header can appear on:

  • all pages
  • the first page only
  • the last page only
  • every page except the first
  • explicit page numbers

Selective header targeting also uses page-aware spacing, so header space is reserved only where the header is actually rendered.

Footers

Footers support:

  • HTML mode
  • text mode
  • trusted HTML content
  • optional logos
  • configurable logo dimensions
  • background and text colors
  • page-number templates

For example:

Page {n} of {total}

Header and footer logos preserve their aspect ratio instead of being stretched into a fixed box.

Static HTML footers are now rendered once and reused across pages instead of being rasterized repeatedly.

In project benchmark fixtures:

HTML footer without logo
7.82s -> 1.43s

HTML footer with logo
11.81s -> 1.29s

Footer-heavy PDF size
~46.7 MB -> ~1.0 MB

These numbers are specific to the benchmark fixtures and should not be treated as universal performance guarantees.

Repeated image optimization

Selectable-text rendering now caches repeated images within a single generation.

If the same image is reused across many pages, the package can avoid repeatedly loading and encoding the same source.

The cache is scoped to one PDF generation and is cleared afterward.

This is especially useful for documents containing repeated logos, icons, product images, or other shared assets.

Resource cleanup

A lot of work also went into cleaning temporary resources earlier.

The generation pipeline now handles cleanup of things such as:

  • temporary cloned DOM nodes
  • temporary canvases
  • encoded image data
  • per-generation image caches
  • rendering state

Canvas backing stores are also released when possible after they are no longer needed.

Repeated-generation and isolation tests were added to help detect regressions in cleanup behavior.

Browser responsiveness

Large PDF generation is naturally expensive, especially in Canvas mode.

The pipeline now yields between selected expensive operations so the browser gets opportunities to process UI work while generation is running.

Combined with cancellation and page-aware progress, this makes long-running generation easier to integrate into real applications.

Watermarks

Watermarks can be:

  • text or image
  • foreground or background
  • single or repeated
  • rotated
  • positioned
  • configured with opacity
  • targeted to selected PDF pages

PDF security

You can optionally generate password-protected PDFs with:

  • user/open password
  • owner password
  • configurable permissions
  • password construction from multiple password parts

Supported permission controls include:

print
modify
copy
annot-forms

Metadata and compression

PDF metadata can include:

  • title
  • author
  • subject
  • keywords
  • creator

jsPDF stream compression can also be enabled when needed.

PDF link annotations are also supported, allowing links in generated documents to remain interactive instead of becoming plain visual text.

Safer filenames

Generated filenames are normalized before download.

This includes handling invalid filename characters, whitespace, reserved Windows names, trailing dots or spaces, and overly long filenames.

Unicode filenames are still preserved.

A note about encrypted raster PDFs

While profiling larger secured documents, I found that encrypted raster PDFs could retain significantly more browser memory than equivalent unencrypted PDFs.

I reproduced the behavior directly with jsPDF outside the main vue3-pdf-export rendering pipeline.

That means a substantial part of this behavior appears to come from the underlying PDF library rather than from the Vue component itself.

I opened an upstream jsPDF issue with the reproduction and benchmark details:

https://github.com/parallax/jsPDF/issues/4017

For large secured documents, selectable-text mode can sometimes be a useful alternative because text-based PDFs are generally much smaller than equivalent rasterized documents.

This is still workload-dependent, so applications generating large encrypted PDFs should test their own representative content.

Testing and performance work

The project now includes automated testing around:

  • unit behavior
  • Chromium
  • Firefox
  • WebKit
  • large documents
  • Canvas rendering
  • selectable-text rendering
  • cancellation and recovery
  • image-heavy documents
  • repeated images
  • footer optimization
  • browser cleanup
  • security behavior
  • memory measurements
  • repeated generation
  • stress testing

The current unit suite contains:

19 test files
106 tests

There are also dedicated benchmark and Playwright workflows for performance-heavy scenarios.

For heavy PDF tests, I intentionally run browser work sequentially where appropriate so multiple workers do not distort memory measurements.

Why I built a Vue layer instead of replacing the underlying libraries

The goal was never to pretend that html2pdf.js, html2canvas, or jsPDF do not exist.

Those libraries still provide important pieces of the Canvas rendering pipeline.

The goal of vue3-pdf-export is to provide a Vue-oriented layer around PDF generation so common production requirements do not need to be rebuilt for every application.

At the same time, developers can still pass advanced html2pdf.js and jsPDF options when the higher-level API is not enough.

The selectable-text renderer provides another option for cases where real PDF text matters more than exact Canvas-style browser rendering.

Current release

The current release is:

vue3-pdf-export v0.4.1

The project has grown significantly since the original release, including:

  • Canvas and selectable-text rendering modes
  • incremental page-by-page Canvas generation
  • large-document regression coverage
  • cancellation and recovery
  • current-page / total-page progress
  • repeated-image caching
  • footer rendering optimization
  • earlier DOM and Canvas cleanup
  • browser responsiveness improvements
  • PDF security
  • metadata and compression
  • clickable links
  • trusted HTML headers and footers
  • logo support
  • watermarks
  • filename normalization
  • expanded unit, browser, stress, and performance testing

Try it

🌐 Live Playground

Explore the features and generate PDFs directly in the browser:

vue3-pdf-export.vercel.app

📦 npm Package

Install and use the package in your Vue 3 project:

npmjs.com/package/vue3-pdf-export

The package is available for Vue 3 applications and continues to evolve around real-world PDF generation requirements.

If you are building a Vue application that needs client-side PDF generation, I would love to hear about the kinds of documents you are generating and the edge cases you run into.

If you find a bug, performance cases, or have an idea for an API improvement, feel free to let me know.

4 Comments

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

More Posts

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

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

Dharanidharan - Feb 9

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

Everyone says DeepSeek is cheaper, but I got tired of guessing the exact math. So I built a calculat

abarth23 - Apr 27

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20
chevron_left
706 Points10 Badges
2Posts
4Comments
1Connections
Software Engineer building
enterprise SaaS platforms, multi-tenant systems, secure
authentication pl... Show more

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!