Api

Full README Reference

Read the complete vue-pdf-export package reference, including installation, rendering modes, API options, output formats, styling and production guidance.

This page mirrors the maintained package README so no public option or guidance is hidden from the documentation site.

A Vue + TypeScript HTML-to-PDF component and composable for browser applications.

vue-pdf-export supports two rendering modes:

  • Canvas mode - maximum HTML/CSS visual fidelity through html2pdf.js + html2canvas + jsPDF.
  • Selectable text mode - browser-side vector PDF rendering with real selectable/searchable/copyable text, clickable PDF links, password protection, metadata, compression, page-aware headers/footers, and watermarks.

It also includes manual and automatic pagination, preview, download, print, export-only page/body styling, data-pdf-ignore, custom export CSS, external print stylesheets, forced light/dark themes, width auto-fit, safe filename normalization, progress stages, custom loaders, generation metrics, current-page progress, cancellation, page targeting, PDF permissions, SSR-safe imports, large-document optimizations, and advanced renderer configuration.

Default package behavior remains renderMode="canvas" for backwards compatibility. The live playground may choose a different default for demonstration purposes.

Highlights

  • Browser-side PDF generation with no package-managed upload service
  • Canvas and selectable-text rendering
  • Blob, ArrayBuffer, raw Base64, and PDF data-URL output helpers
  • Incremental page-by-page Canvas rendering when page-break markers are present
  • Cooperative cancellation for long-running exports
  • Page-aware progress with currentPage / totalPages
  • Repeated-image caching in selectable-text rendering
  • Temporary DOM/canvas cleanup between expensive rendering steps
  • Password protection, owner password, PDF permission flags, metadata, and compression
  • Headers, footers, logos, watermarks, page targeting, and clickable links
  • Automated regression coverage for 10 / 25 / 50 / 100-page documents
  • Large-document validation across 5 / 10 / 25 / 50 / 100 / 200 / 500 / 1000-page stress scenarios
  • Export-only page/body styling without mutating the visible Vue DOM
  • data-pdf-ignore for screen-only UI that must stay out of generated PDFs
  • Per-page styling callbacks, scoped CSS injection, external print stylesheets, forced themes, and width auto-fit
  • Opt-in total/stage/per-page generation timing metrics
  • SSR-safe package imports with Nuxt 3 and Nuxt 4 fixture coverage

Documentation

Live Demo

Try the interactive playground:

The playground demonstrates:

  • Canvas and selectable-text rendering modes
  • Real selectable/searchable text in selectable mode
  • Clickable PDF hyperlinks
  • PDF preview
  • Download
  • Print
  • Programmatic Blob, ArrayBuffer, and Base64 output
  • Manual pagination
  • Automatic height-based pagination
  • Page-break protection helpers
  • Headers and footers
  • Plain-text and trusted HTML header/footer content
  • Optional header/footer logos
  • Custom logo sizing with preserved aspect ratio
  • Header page targeting
  • Page-aware selective header spacing
  • Footer page numbering with Page {n} of {total}
  • Selectable header/footer text in selectable mode
  • Text and image watermarks
  • Background and foreground watermarks
  • Per-page watermark targeting
  • Password-protected PDFs
  • User/open and owner passwords
  • Combined password parts
  • PDF permissions
  • PDF metadata
  • PDF stream compression
  • Numeric progress
  • Named progress stages
  • Custom loaders
  • Advanced html2pdf.js, html2canvas, and jsPDF options
  • data-pdf-ignore export filtering
  • Export-only page/body backgrounds, classes, and inline style objects
  • Per-page pageStyle(context) callbacks
  • Scoped custom PDF CSS text
  • External print stylesheet URLs
  • Forced current/light/dark export themes
  • Width auto-fit/scaling
  • Opt-in total, stage, and per-page generation metrics

Features

Core PDF workflow

  • vue component API
  • Vue composable API
  • TypeScript declarations
  • Browser-side PDF generation
  • Two rendering modes: canvas and text
  • PDF Blob output
  • PDF ArrayBuffer output
  • Raw Base64 output
  • Optional data:application/pdf;base64,... output
  • PDF preview modal
  • Browser download
  • Programmatic print
  • Blob URL cleanup
  • Safe filename normalization

Rendering modes

  • canvas - existing html2canvas/html2pdf pipeline for strongest browser CSS fidelity
  • text - vector/browser-layout renderer with selectable/searchable PDF text
  • Default mode is canvas
  • Same public <Html2Pdf> API for both modes
  • Same Blob/preview/download/print flow for both modes
  • Blob output can be serialized to ArrayBuffer or Base64 without a second PDF render
  • Shared header/footer/watermark/metadata post-processing
  • Password protection available in both modes
  • Clickable links available when PDF links are enabled

Pagination and layout

  • Manual pagination
  • Automatic height-based pagination
  • Existing .html2pdf__page-break marker works with both render modes
  • Page-break protection helpers
  • Custom pagebreak.avoid selectors
  • Automatic page-break reset
  • Manual breaks preserved when automatic breaks are reset
  • Page-aware automatic pagination for selectively targeted headers
  • Deterministic export-width support
  • data-pdf-ignore / data-pdf-ignore="true" removes screen-only subtrees from export
  • data-pdf-ignore="false" explicitly keeps content

Export-only styling

  • Master styling.enabled switch
  • Page background
  • Body background
  • Page/body class names
  • Page/body inline style objects
  • Per-page pageStyle({ pageNumber, totalPages, orientation }) callback
  • Export-only cssText, scoped to the temporary export tree
  • External stylesheetUrls / printStylesheetUrls
  • Forced current, light, or dark export theme
  • Width auto-fit / shrink-to-fit controls
  • Temporary style/theme/export-tree cleanup after generation
  • Styling stays off the visible/source DOM

Headers

  • Header on all pages by default when enabled
  • Header targeting:
    • all
    • first
    • last
    • except-first
    • explicit page arrays
  • Page-aware header spacing
  • Plain-text header content
  • Trusted HTML header content
  • Optional logo
  • Logo URL or data URL
  • Custom logo width and height
  • Aspect-ratio-safe logo rendering
  • Header background color
  • Header text color
  • Selectable header text in selectable-text mode

Footers

  • Footer on every page when enabled
  • HTML footer mode
  • Text footer mode
  • Trusted HTML footer content
  • Built-in page numbering
  • Optional logo
  • Logo URL or data URL
  • Custom logo width and height
  • Aspect-ratio-safe logo rendering
  • Footer background color
  • Footer text color
  • Legacy footer compatibility
  • Selectable footer text and page-number text in selectable-text mode

Security and document options

  • User/open password
  • Owner password
  • Password parts
  • Configurable PDF permissions
  • Password protection in both render modes
  • PDF metadata
  • PDF stream compression

Watermarks

  • Text watermarks
  • Image watermarks
  • Background layer
  • Foreground layer
  • Single mode
  • Repeated/tiled mode
  • Per-page targeting
  • Opacity
  • Rotation
  • Position
  • Sizing
  • Horizontal/vertical spacing
  • Real PDF link annotations
  • <a href="..."> support
  • Clickable links in generated PDFs when links are enabled
  • Link rectangles follow pagination in selectable-text mode
  • Link text remains selectable in selectable-text mode

Developer experience

  • Built-in loader
  • Custom loader slot
  • Numeric progress event
  • Named progress stages
  • CORS-aware image handling
  • Advanced html2pdf configuration
  • Filename sanitization
  • Cross-browser validation across Chromium, Firefox, and WebKit
  • Opt-in generation metrics with total/stage/per-page timings
  • SSR-safe root imports
  • Nuxt 3 and Nuxt 4 SSR compatibility validation
  • Public output serializers for Blob → ArrayBuffer / Base64 conversion

Installation

npm install vue-pdf-export

Runtime PDF dependencies are installed automatically.

Import

TypeScript:

import {
  Html2Pdf,
  type PdfMetadataOptions,
  type PdfProgressState,
  type PdfRenderMode,
  type PdfSecurityOptions,
  type PdfStylingOptions,
  type PdfStyle,
  type PdfPageContext,
  type PdfGenerationMetrics,
  type WatermarkOptions,
} from 'vue-pdf-export'

import 'vue-pdf-export/style.css'

JavaScript:

import { Html2Pdf } from 'vue-pdf-export'
import 'vue-pdf-export/style.css'

Quick Start

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

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

async function generatePdf() {
  const blob = await pdfRef.value?.generatePdf()
  console.log(blob)
}
</script>

<template>
  <button type="button" @click="generatePdf">Generate PDF</button>

  <Html2Pdf
    ref="pdfRef"
    filename="my-report"
    render-mode="canvas"
    :manual-pagination="true"
    :enable-download="true"
  >
    <template #pdf-content>
      <div>
        <div class="vpe-reference-logo">My Report</div>
        <button data-pdf-ignore>Visible in the app, excluded from the PDF</button>
        <p>This content will be rendered into the PDF.</p>
      </div>
    </template>
  </Html2Pdf>
</template>

Rendering Modes

Canvas mode

Canvas mode is the default and uses the package's existing html2pdf.js / html2canvas / jsPDF pipeline.

<Html2Pdf render-mode="canvas" />

Use it when the highest possible browser CSS fidelity is more important than selectable body text.

Best for:

  • complex CSS
  • gradients
  • shadows
  • transforms
  • custom web fonts
  • visual dashboards
  • designs where browser appearance is the priority

Body content is rasterized, so normal body text is not selectable/searchable.

Incremental Canvas rendering

When the export DOM contains .html2pdf__page-break markers, Canvas mode uses an incremental page-by-page renderer instead of capturing the entire document as one giant canvas.

This path:

  • clones and renders one logical PDF page at a time
  • keeps raster dimensions page-bounded
  • releases temporary page DOM after html2canvas finishes reading it
  • releases canvas backing stores after insertion into jsPDF
  • reports page-level progress
  • yields to the browser between expensive steps
  • supports cooperative cancellation before the next page begins

Canvas documents without page-break markers keep the legacy html2pdf.js worker for backwards compatibility.

Selectable text mode

<Html2Pdf render-mode="text" />

Selectable text mode converts the live browser layout into PDF vector/text operations instead of turning the entire document into a screenshot.

It supports:

  • selectable body text
  • searchable body text
  • copyable body text
  • selectable header/footer text
  • selectable footer page numbers
  • clickable PDF hyperlinks
  • images
  • backgrounds
  • borders
  • basic border radius
  • text color, weight, italic, decoration, alignment, and spacing
  • multi-page output
  • manual page-break markers
  • PDF password protection
  • PDF permissions
  • metadata
  • compression
  • headers, footers, and watermarks

Example:

<Html2Pdf render-mode="text" :security="security" :metadata="metadata" :compress="true">

  <template #pdf-content>
    <main>
      <div class="vpe-reference-logo">Selectable report</div>
      <p>You can select and search this text in the generated PDF.</p>

      <a href="https://www.npmjs.com/package/vue-pdf-export">
        Open vue-pdf-export on npm
      </a>
    </main>
  </template>
</Html2Pdf>

Selectable-text mode intentionally favors real PDF text over pixel-perfect parity with every browser CSS feature. See Selectable Text Mode Notes.

Component Registration

With <script setup>:

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

No components registration is required.

Options API

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

export default defineComponent({
  components: {
    Html2Pdf,
  },
})
</script>

Global registration

import { createApp } from 'vue'
import App from './App.vue'
import { Html2Pdf } from 'vue-pdf-export'
import 'vue-pdf-export/style.css'

const app = createApp(App)

app.component('Html2Pdf', Html2Pdf)
app.mount('#app')

Preview & Download

Preview only:

<Html2Pdf :enable-download="false" :preview-modal="true" />

Download only:

<Html2Pdf :enable-download="true" :preview-modal="false" />

Download and preview:

<Html2Pdf :enable-download="true" :preview-modal="true" />

enableDownload and previewModal are independent.

Print

await pdfRef.value?.print()

Print reuses the generated PDF result and opens the browser's native print flow through a temporary same-page iframe. It does not require a popup/new tab, and the temporary print iframe is cleaned up after the print lifecycle.

Browser print dialogs and print lifecycle details are still controlled by the browser/OS.

Pagination

Manual pagination

<Html2Pdf :manual-pagination="true">
  <template #pdf-content>
    <section>Page one</section>

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

    <section>Page two</section>
  </template>
</Html2Pdf>

Recommended helper CSS:

.html2pdf__page-break {
  display: block;
  height: 0;
  clear: both;
}

Do not add break-before or page-break-before to this helper while legacy page-break mode is active. Doing so can create duplicate breaks or blank pages.

The same .html2pdf__page-break marker is recognized by selectable-text mode, so consumers do not need a second pagination API.

Automatic pagination

<Html2Pdf :manual-pagination="false" :paginate-elements-by-height="1100">

  <template #pdf-content>
    <div>
      <section>Block 1</section>
      <section>Block 2</section>
      <section>Block 3</section>
    </div>
  </template>
</Html2Pdf>

The component measures direct child elements and inserts temporary automatic page-break markers when the configured height would be exceeded.

Prevent content from splitting

Default protected selectors:

.pdf-keep-together
.pdf-no-break
img

Example:

<div class="pdf-keep-together">
  <h2>Invoice Summary</h2>
  <p>Keep this block together.</p>
</div>

You can extend page-break avoidance:

<Html2Pdf
  :html-to-pdf-options="{
    pagebreak: {
      avoid: ['.custom-card'],
    },
  }"
/>

Your selectors are merged with package defaults.

No-break helpers are best-effort. A block that is taller than the usable PDF page area may still need to split.

Exclude Content from PDF

Use data-pdf-ignore for controls, helper UI, debug panels, buttons, or any subtree that should stay visible in the application but must not be included in the generated PDF.

<div>
  <div class="vpe-reference-logo">Invoice</div>

  <button data-pdf-ignore>
    Edit invoice
  </button>

  <aside data-pdf-ignore="true">
    This entire subtree is excluded from the PDF.
  </aside>

  <p data-pdf-ignore="false">
    Explicit false keeps this content in the PDF.
  </p>
</div>

Semantics:

MarkupPDF behavior
data-pdf-ignoreexcluded
data-pdf-ignore="true"excluded
data-pdf-ignore="false"included

The visible/source Vue DOM is not hidden or removed. Filtering is performed on the temporary export tree. Ignored content is also excluded from export pagination measurements.

For Vue bindings:

<button :data-pdf-ignore="hideActionInPdf">
  App action
</button>

When the value is false, the element remains in the PDF.

PDF Styling

Use the styling object when the generated PDF needs presentation rules that should not change the visible application.

import type {
  PdfPageContext,
  PdfStyle,
  PdfStylingOptions,
} from 'vue-pdf-export'

const bodyStyle: PdfStyle = {
  padding: '16px',
  fontSize: '14px',
  color: '#17324A',
}

const styling: PdfStylingOptions = {
  enabled: true,

  pageBackground: '#FFF8E7',
  bodyBackground: '#F8FAFC',

  pageClass: 'invoice-page',
  bodyClass: 'invoice-body',

  pageStyle: ({ pageNumber }: PdfPageContext) => ({
    padding: '24px',
    border: '2px solid #DCE7E9',
    borderRadius: '12px',
    backgroundColor: pageNumber === 1 ? '#FFF7ED' : '#FFFFFF',
  }),

  bodyStyle,

  cssText: `
    .invoice-title {
      font-size: 28px;
      letter-spacing: -0.02em;
    }

    .screen-only {
      display: none;
    }
  `,

  printStylesheetUrls: [
    '/pdf/invoice-print.css',
  ],

  theme: 'light',

  autoFit: {
    enabled: true,
    minScale: 0.6,
    maxScale: 1,
    allowUpscale: false,
  },
}
<Html2Pdf :styling="styling">
  <template #pdf-content>
    <article>
      <h1 class="invoice-title">Invoice</div>
      <button class="screen-only">Application action</button>
    </article>
  </template>
</Html2Pdf>

Styling master switch

const styling: PdfStylingOptions = {
  enabled: false,
}

When enabled is explicitly false, Step 8 styling is not passed into PDF generation.

Page and body styling

The object supports:

OptionTypePurpose
enabledbooleanMaster styling switch.
pageBackgroundstringPDF page surface background.
bodyBackgroundstringInner body/content surface background.
pageClassstringExport-only page class names.
bodyClassstringExport-only body class names.
pageStylePdfStyle | PdfStyleResolverStatic or per-page page styles.
bodyStylePdfStyleStatic body styles.
cssTextstringExport-only scoped CSS text.
stylesheetUrlsstring[]External export stylesheet URLs.
printStylesheetUrlsstring[]External print/export stylesheet URLs.
theme'current' | 'light' | 'dark'Export theme.
autoFitboolean | PdfAutoFitOptionsWidth auto-fit/shrink behavior.

Per-page style callback

const styling: PdfStylingOptions = {
  enabled: true,

  pageStyle: ({ pageNumber, totalPages, orientation }) => ({
    padding: '24px',
    border: '1px solid #DCE7E9',
    backgroundColor:
      pageNumber === 1
        ? '#FFF7ED'
        : pageNumber === totalPages
          ? '#F0FDF4'
          : '#FFFFFF',
  }),
}

Paint properties can vary per page. Keep layout-changing values such as page padding/border width consistent across pages so pagination geometry remains predictable.

Custom CSS text

cssText is scoped to the temporary export tree rather than being installed as global application CSS.

const styling: PdfStylingOptions = {
  enabled: true,

  cssText: `
    .invoice-total {
      font-size: 24px;
      font-weight: 800;
    }
  `,
}

The temporary style element is removed after generation.

External print stylesheets

const styling: PdfStylingOptions = {
  enabled: true,

  printStylesheetUrls: [
    '/pdf/base.css',
    'https://cdn.example.com/invoice-print.css',
  ],
}

Browser fetch/CORS rules apply to remote stylesheets. Relative url(...) values are rebased against the stylesheet URL. Nested @import rules are intentionally skipped; list imported stylesheets separately.

Forced theme

const styling: PdfStylingOptions = {
  enabled: true,
  theme: 'dark',
}

Supported values:

current
light
dark

Theme markers exist only on the export tree.

Auto-fit / scaling

const styling: PdfStylingOptions = {
  enabled: true,

  autoFit: {
    enabled: true,
    minScale: 0.6,
    maxScale: 1,
    allowUpscale: false,
  },
}

Auto-fit is designed primarily to shrink horizontally overflowing content into the available export width. It does not upscale unless allowUpscale is explicitly enabled.

Styling cleanup and SSR

The package does not touch browser globals at module import time. Temporary export trees, scoped style elements, theme markers, and styling attributes are removed after generation.

Root imports are SSR-safe; actual PDF generation remains browser-only.

Enable PDF links through htmlToPdfOptions.enableLinks:

const htmlToPdfOptions = {
  enableLinks: true,
}
<Html2Pdf :html-to-pdf-options="htmlToPdfOptions">
  <template #pdf-content>
    <a href="https://www.npmjs.com/package/vue-pdf-export">
      Open vue-pdf-export on npm
    </a>
  </template>
</Html2Pdf>

In selectable-text mode, anchors are converted into real PDF link annotations, so compatible PDF viewers can show the link cursor and open the URL when clicked.

For best selectable-text rendering, keep PDF link styling straightforward. Very large letter-spacing, transforms, or highly compressed fixed-width buttons can create visual differences because browser and PDF font metrics are not identical.

Recommended PDF-friendly link CSS:

.pdf-link {
  display: inline-flex;
  align-items: center;
  width: auto;
  max-width: 100%;
  padding: 8px 12px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0;
  white-space: nowrap;
  text-decoration: underline;
}

Basic header

<Html2Pdf
  :inject-header="true"
  :header-height="58"
  header-text="My Company"
  :header-logo-enabled="true"
  header-logo-url="/logo.png"
  :header-logo-width="120"
  :header-logo-height="32"
  header-background-color="#F8F9FA"
  header-text-color="#5F6368"
/>

Header text

<Html2Pdf :inject-header="true" header-text="Quarterly Report" />

In selectable-text mode, plain header text is added as real PDF text and remains selectable/searchable.

Header HTML

Use headerHtml for trusted developer-controlled markup:

<Html2Pdf
  :inject-header="true"
  header-html="<strong>Quarterly <span style='color:#13B981'>Report</span></strong>"
/>

When headerHtml is non-empty, it takes precedence over headerText.

headerHtml is intentionally trusted HTML. Do not pass unsanitized user input.

Selectable-text mode preserves common inline header styling such as color, bold, italic, and basic font-size changes while keeping the text selectable. Complex nested layout/CSS may still be simplified; use canvas mode when exact rich-HTML header fidelity is more important than text selection.

Header logos are enabled by default.

<Html2Pdf :inject-header="true" :header-logo-enabled="false" header-text="Text-only header" />

The header itself remains enabled.

Header logo sizing

<Html2Pdf
  :inject-header="true"
  header-logo-url="/wide-logo.png"
  :header-logo-width="160"
  :header-logo-height="36"
/>

headerLogoWidth and headerLogoHeight define the maximum logo box. The original image aspect ratio is preserved.

Default logo box:

28 × 28 CSS pixels

Header page targeting

First page:

<Html2Pdf :inject-header="true" header-pages="first" />

Last page:

<Html2Pdf :inject-header="true" header-pages="last" />

Skip first:

<Html2Pdf :inject-header="true" header-pages="except-first" />

Explicit pages:

<Html2Pdf :inject-header="true" :header-pages="[1, 3, 5]" />

Supported type:

type PdfPageTarget = 'all' | 'first' | 'last' | 'except-first' | number[]

Page-aware header spacing

Selective headers reserve header space only on pages that actually receive the header.

header-pages="first"

Page 1 -> header + header space
Page 2 -> no header + no extra header space
Page 3 -> no header + no extra header space

Automatic pagination also accounts for this selective spacing.

Any top margin explicitly supplied through htmlToPdfOptions.margin remains independent and is not removed.

Header props

PropTypeDefaultDescription
injectHeaderbooleanfalseEnable header rendering.
headerHeightnumber58Header height in the current jsPDF unit.
headerTextstring''Plain-text header content.
headerHtmlstring''Trusted HTML header content; takes precedence over headerText.
headerLogoEnabledbooleantrueShow/hide the header logo.
headerLogoUrlstring''Logo URL.
headerLogoDataUrlstring''Logo data URL/base64.
headerLogoWidthnumber28Maximum logo width in CSS pixels.
headerLogoHeightnumber28Maximum logo height in CSS pixels.
headerBackgroundColorstring#F8F9FAHeader background.
headerTextColorstring#5F6368Header text/fallback color.
headerPagesPdfPageTargetall-page behaviorHeader page targeting.
<Html2Pdf
  :inject-footer="true"
  :footer-height="58"
  footer-mode="html"
  footer-text="My Company"
  footer-template="Page {n} of {total}"
/>

If injectFooter is false, the package does not reserve footer-specific bottom space. Any explicit bottom margin supplied through htmlToPdfOptions.margin remains.

<Html2Pdf
  :inject-footer="true"
  footer-mode="html"
  footer-html="<strong>My Company</strong>"
  footer-template="Page {n} of {total}"
/>

When footerHtml is non-empty in HTML mode, it takes precedence over footerText.

footerHtml is intentionally trusted HTML. Do not pass unsanitized user input.

In selectable-text mode, common inline footer styling such as color, bold, italic, and basic font-size changes is preserved as selectable PDF text where supported. Complex HTML layout may be simplified.

<Html2Pdf
  :inject-footer="true"
  footer-mode="text"
  footer-text="My Company"
  footer-template="Page {n} of {total}"
/>

In selectable-text mode, footer text and the page-number template are emitted as selectable PDF text.

Footer logos are enabled by default.

<Html2Pdf
  :inject-footer="true"
  :footer-logo-enabled="false"
  footer-mode="text"
  footer-text="Text-only footer"
/>
<Html2Pdf
  :inject-footer="true"
  :footer-logo-enabled="true"
  footer-logo-url="/wide-logo.png"
  :footer-logo-width="150"
  :footer-logo-height="30"
/>

The logo keeps its original aspect ratio.

Default logo box:

26 × 26 CSS pixels

Supported placeholders:

{n}
{total}

Example:

Page {n} of {total}

Output:

Page 2 of 5

The footer template is the package's built-in page-numbering mechanism. There is no separate dedicated page-numbering API.

The old prop remains supported:

<Html2Pdf :use-footer-component="true" />

Prefer:

<Html2Pdf footer-mode="html" />

or:

<Html2Pdf footer-mode="text" />

When footerMode is explicitly provided, it takes priority over useFooterComponent.

PropTypeDefaultDescription
injectFooterbooleanfalseEnable footer rendering.
footerHeightnumber58Footer height in the current jsPDF unit.
footerMode`'html' \'text'`compatibility-resolved
footerTextstring''Plain-text footer content / HTML-mode fallback.
footerHtmlstring''Trusted HTML footer content.
footerTemplatestringPage {n} of {total}Page number/footer template.
footerLogoEnabledbooleantrueShow/hide footer logo.
footerLogoUrlstring''Logo URL.
footerLogoDataUrlstring''Logo data URL/base64.
footerLogoWidthnumber26Maximum logo width in CSS pixels.
footerLogoHeightnumber26Maximum logo height in CSS pixels.
footerBackgroundColorstring#F8F9FAFooter background.
footerTextColorstring#5F6368Footer text/fallback color.
useFooterComponentbooleantrueDeprecated compatibility option.

Security

Create password-protected PDFs with the security prop.

import type { PdfSecurityOptions } from 'vue-pdf-export'

const security: PdfSecurityOptions = {
  enabled: true,
  userPassword: 'open123',
  ownerPassword: 'owner456',
  permissions: ['print', 'copy'],
}
<Html2Pdf :security="security" />

Password protection works in both canvas and selectable-text render modes.

Permission flags

type PdfPermission = 'print' | 'modify' | 'copy' | 'annot-forms'
PermissionWhat it allows
printAllows printing when the PDF viewer enforces document permissions.
copyAllows text/content copying and extraction when the viewer enforces permissions.
modifyAllows document modification/editing when supported by the viewer.
annot-formsAllows annotations and form-related changes when supported by the viewer.

PDF permission enforcement depends on the PDF viewer. Password encryption is part of the PDF itself, while permission flags rely on viewer compliance.

Security guidance

The package can generate encrypted/password-protected PDFs, but PDF security should be treated as one layer of application security rather than a replacement for access control.

Important boundaries:

  • User/open password controls opening an encrypted PDF.
  • Owner password is used by the PDF encryption layer for owner-level access/permission configuration.
  • Permission flags are viewer-dependent. Some PDF viewers may not enforce them identically.
  • headerHtml and footerHtml are intentionally trusted developer HTML. Do not pass unsanitized user-controlled HTML.
  • Remote images are fetched by the browser. Use trusted origins, correct CORS headers, and avoid exposing secrets in image URLs.
  • Passwords exist in browser memory while a PDF is being generated. Do not hard-code sensitive production passwords into public frontend source.
  • Generated Blob URLs are cleaned up by the component/composable lifecycle, but applications should still avoid retaining generated Blobs longer than necessary.

vue-pdf-export is a client-side library and does not require a package-operated server to generate a PDF. Your application, remote assets, analytics, and surrounding infrastructure may still perform their own network requests.

Password parts

const security = {
  enabled: true,
  passwordParts: ['15081999', 4821],
  passwordSeparator: '-',
}

This creates one final open password:

15081999-4821

passwordParts do not create multiple independent passwords.

When security is omitted, compatible raw htmlToPdfOptions.jsPDF.encryption values are preserved.

When security.enabled is explicitly false, encryption is removed.

Metadata

import type { PdfMetadataOptions } from 'vue-pdf-export'

const metadata: PdfMetadataOptions = {
  title: 'Invoice #1024',
  author: 'Acme Inc.',
  subject: 'Customer invoice',
  keywords: ['invoice', '2026'],
  creator: 'vue-pdf-export',
}
<Html2Pdf :metadata="metadata" />

Metadata is written to PDF document properties and is not rendered as normal visible page content.

The direct metadata prop takes priority over htmlToPdfOptions.metadata.

Compression

<Html2Pdf :compress="true" />

compress controls jsPDF stream compression.

It does not lower html2canvas resolution or JPEG image quality.

If omitted, compatible raw htmlToPdfOptions.jsPDF.compress is preserved.

Watermarks

Text watermark

import type { WatermarkOptions } from 'vue-pdf-export'

const watermark: WatermarkOptions = {
  enabled: true,
  layer: 'background',
  type: 'text',
  mode: 'repeat',
  text: 'CONFIDENTIAL',
  opacity: 0.1,
  rotation: -35,
  fontSize: 36,
  gapX: 100,
  gapY: 90,
}
<Html2Pdf :watermark="watermark" />

Foreground watermark

const watermark = {
  enabled: true,
  layer: 'foreground',
  type: 'text',
  mode: 'single',
  text: 'DRAFT',
  opacity: 0.15,
  rotation: -30,
  fontSize: 60,
  x: 0.5,
  y: 0.5,
}

Foreground watermarks are drawn onto final PDF pages with jsPDF.

Image watermark

const watermark = {
  enabled: true,
  layer: 'foreground',
  type: 'image',
  mode: 'single',
  imageUrl: '/logo.png',
  opacity: 0.1,
  rotation: -25,
  width: 180,
  x: 0.5,
  y: 0.5,
}

A data URL can also be used:

const watermark = {
  enabled: true,
  type: 'image',
  imageDataUrl: 'data:image/png;base64,...',
}

imageDataUrl takes priority over imageUrl.

Per-page watermark targeting

const watermark = {
  enabled: true,
  type: 'text',
  text: 'INTERNAL',
  pages: 'except-first',
}

Explicit pages:

const watermark = {
  enabled: true,
  text: 'INTERNAL',
  pages: [1, 3, 5],
}

Supported targets:

all
first
last
except-first
number[]

For selective targeting, a requested background watermark may be applied during final PDF post-processing so selected-page behavior can be preserved.

Watermark options

OptionTypeDescription
enabledbooleanEnable/disable watermark.
type`'text' \'image'`
layer`'background' \'foreground'`
mode`'single' \'repeat'`
textstringText content.
fontSizenumberText size.
fontFamilystringFont family.
fontWeight`string \number`
fontStyle`'normal' \'bold' \
colorstringWatermark color.
imageUrlstringImage URL.
imageDataUrlstringData URL/base64 image.
widthnumberImage width.
heightnumberOptional image height.
opacitynumberClamped to 0..1.
rotationnumberRotation in degrees.
xnumberHorizontal position.
ynumberVertical position.
gapXnumberHorizontal repeat gap.
gapYnumberVertical repeat gap.
pagesPdfPageTargetTarget pages.

Loader & Progress

Built-in loader

<Html2Pdf :show-loader="true" loader-text="Generating PDF..." />

Disable it:

<Html2Pdf :show-loader="false" />

Numeric progress

<Html2Pdf @progress="onProgress" />
function onProgress(value: number) {
  console.log(value)
}

This is generation-stage progress, not byte-level network progress.

Rich progress stages

<Html2Pdf @progress-stage="onProgressStage" />
import type { PdfProgressState } from 'vue-pdf-export'

function onProgressStage(state: PdfProgressState) {
  console.log(state.stage, state.progress)
}

Current stages:


idle

pagination

images

preparing

rendering

watermark

header

footer

metadata

serializing

complete

error

Custom loader

<Html2Pdf>
  <template #loader="{ progress, loading, stage, progressState }">
    <div v-if="loading">
      {{ stage }} - {{ progress }}%
    </div>
  </template>
</Html2Pdf>

Slot values:

ValueType
progressnumber
loadingboolean
stagePdfProgressStage
progressStatePdfProgressState

Generation metrics

Generation metrics are opt-in. When disabled, the metrics collector and metrics UI/events should not add generation work.

A completed generation can expose:

  • total generation time
  • stage timings
  • per-page timings
  • render mode
  • page count
  • generated Blob size

The result shape includes optional metrics:

interface GeneratePdfResult {
  pdf: jsPDF
  blob: Blob
  url: string
  metrics?: PdfGenerationMetrics
}

Use metrics for profiling/regression checks rather than as a hard real-time performance guarantee.

Enable metrics on the component:

<Html2Pdf
  :collect-metrics="true"
  @generation-complete="onGenerationComplete"
/>
import type { PdfGenerationCompletePayload } from 'vue-pdf-export'

function onGenerationComplete(payload: PdfGenerationCompletePayload) {
  console.log(payload.metrics)
}

In selectable-text mode, the first page timing can include renderer startup work performed before the first page-complete callback, such as DOM snapshot/layout preparation and initial image materialization. Later page timings are usually closer to pure page drawing time.

Programmatic API

Component methods:

interface Html2PdfInstance {
  generatePdf: () => Promise<Blob>
  generatePdfArrayBuffer: () => Promise<ArrayBuffer>
  generatePdfBase64: (options?: PdfBase64Options) => Promise<string>
  print: () => Promise<void>
  closePreview: () => void
  resetPagination: () => void
  cancelGeneration: () => void
}

Recommended ref:

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

Generate Blob

const blob = await pdfRef.value?.generatePdf()

Blob remains the recommended default for downloads, previews, object URLs, and very large PDFs.

Generate ArrayBuffer

const arrayBuffer = await pdfRef.value?.generatePdfArrayBuffer()

Use ArrayBuffer when you need raw PDF bytes for binary APIs, workers, storage, cryptographic processing, or transport layers that accept binary data.

Generate Base64

Raw Base64:

const base64 = await pdfRef.value?.generatePdfBase64()

PDF data URL:

const dataUrl = await pdfRef.value?.generatePdfBase64({
  dataUrl: true,
})

Base64 is convenient for string-only transports, but it is larger than the original binary payload and requires additional conversion memory. Prefer Blob or ArrayBuffer for large PDFs.

The ArrayBuffer and Base64 methods reuse the normal PDF generation flow and serialize the generated Blob. They do not run a second PDF renderer.

For pure programmatic output, disable automatic browser-side download/preview with :enable-download="false" and :preview-modal="false".

await pdfRef.value?.print()

Close preview

pdfRef.value?.closePreview()

Reset automatic pagination

pdfRef.value?.resetPagination()

This removes only automatically inserted page breaks.

Cancel active generation

pdfRef.value?.cancelGeneration()

cancelGeneration() requests cooperative cancellation of the current export.

  • Incremental Canvas rendering checks cancellation around page capture, encoding, insertion, and browser-yield points.
  • Selectable-text rendering checks cancellation while walking the DOM, materializing images, paginating, and rendering pages.
  • Synchronous browser/jsPDF work such as an already-running toDataURL() or final pdf.output('blob') call cannot be interrupted in the middle; cancellation takes effect at the next safe checkpoint.
  • Cancellation is treated as an expected abort flow and is not emitted as the component's normal error event.

Composable

import { useHtml2Pdf } from 'vue-pdf-export'

const {
  loading,
  progress,
  stage,
  progressState,
  error,
  pdfUrl,
  lastResult,
  generate,
  download,
  print,
  cleanup,
} = useHtml2Pdf()

Output serialization helpers

For advanced workflows, the package also exports serializers that operate on already-generated PDF output:

import {
  pdfBlobToArrayBuffer,
  pdfBlobToBase64,
  serializePdfOutput,
} from 'vue-pdf-export'
const arrayBuffer = await pdfBlobToArrayBuffer(blob)

const rawBase64 = await pdfBlobToBase64(blob)

const dataUrl = await pdfBlobToBase64(blob, {
  dataUrl: true,
})

serializePdfOutput(result, format) accepts:

type PdfOutputFormat = 'blob' | 'arraybuffer' | 'base64'

These helpers serialize existing output and do not re-render the document.

Events

<Html2Pdf
  @progress="onProgress"
  @progress-stage="onProgressStage"
  @startPagination="onStartPagination"
  @hasPaginated="onPaginated"
  @beforeDownload="onBeforeDownload"
  @hasDownloaded="onDownloaded"
  @generation-complete="onGenerationComplete"
  @closed="onClosed"
  @error="onError"
/>
EventPayloadDescription
progressnumberNumeric generation progress.
progressStage / @progress-stagePdfProgressStateNamed stage and progress.
startPaginationnonePagination started.
hasPaginatednonePagination completed.
beforeDownloadBeforeDownloadPayloadEmitted before generation.
hasDownloadedBlobGenerated Blob is available.
generationComplete / @generation-completePdfGenerationCompletePayloadGeneration finished; includes opt-in metrics when enabled.
closednonePreview/generation flow closed.
errorErrorGeneration or print error.

Slots

SlotRequiredDescription
pdf-contentyesHTML converted to PDF.
loadernoCustom loader with { progress, loading, stage, progressState }.

Core Props

PropTypeDefaultDescription
renderMode`'canvas' \'text'`'canvas'
showLayoutbooleanfalseShow export layout.
floatLayoutbooleanfalseUse fixed/floating export layout.
enableDownloadbooleantrueDownload after generation.
previewModalbooleanfalseShow PDF preview modal.
filenamestringtimestamp fallbackPDF filename.
pdfQualitynumber2Default html2canvas scale; mainly relevant to canvas mode/images.
pdfFormatPDF format'a4'PDF format.
pdfOrientation`'portrait' \'landscape'`'portrait'
pdfContentWidthstring'800px'Export wrapper width.
htmlToPdfOptionsHtml2PdfOptionspackage defaultsAdvanced options.
metadataPdfMetadataOptionsundefinedPDF metadata.
compressbooleanundefinedjsPDF stream compression override.
securityPdfSecurityOptionsundefinedPassword/security configuration.
manualPaginationbooleanfalseManual pagination mode.
paginateElementsByHeightnumberrequired for auto modeAutomatic page height.
showLoaderbooleantrueShow loader.
loaderTextstring'Generating PDF...'Loader text.
watermarkWatermarkOptionsdisabledWatermark configuration.
stylingPdfStylingOptionsundefinedExport-only page/body styling, CSS, theme and auto-fit controls.
collectMetricsbooleanfalseCollect total/stage/per-page generation metrics.

Header and footer props are documented in their dedicated sections.

Advanced html2pdf Options

const htmlToPdfOptions = {
  margin: [0, 0, 0, 0],
  enableLinks: true,

  pagebreak: {
    mode: ['css', 'legacy'],
  },

  image: {
    type: 'jpeg',
    quality: 1,
  },

  html2canvas: {
    scale: 3,
    useCORS: true,
    backgroundColor: '#FFFFFF',
  },

  jsPDF: {
    unit: 'px',
    format: 'a4',
    orientation: 'p',
    hotfixes: ['px_scaling'],
    optimizeForBrowser: true,
  },
}
<Html2Pdf :html-to-pdf-options="htmlToPdfOptions" />

Important precedence rules:

  • metadata overrides raw htmlToPdfOptions.metadata when provided.
  • compress overrides raw htmlToPdfOptions.jsPDF.compress when provided.
  • security.enabled: true overrides raw jsPDF encryption.
  • security.enabled: false removes raw encryption.
  • Non-empty headerHtml takes precedence over headerText.
  • In HTML footer mode, non-empty footerHtml takes precedence over footerText.
  • headerLogoEnabled controls only header logo visibility.
  • footerLogoEnabled controls only footer logo visibility.
  • Explicit user margins remain even when header/footer rendering is disabled.
  • enableLinks: true enables generated PDF link annotations.

Filename Normalization

import { normalizePdfFilename } from 'vue-pdf-export'

normalizePdfFilename('invoice:/customer?2026')
// invoice--customer-2026.pdf

Normalization:

  • adds exactly one .pdf extension
  • prevents duplicate .pdf
  • sanitizes unsafe filename/path characters
  • trims trailing dots and spaces
  • falls back to a timestamp for empty names
  • protects Windows reserved device names such as CON and LPT1
  • preserves Unicode
  • limits excessively long base names

Examples:

normalizePdfFilename('report')
// report.pdf

normalizePdfFilename('report.PDF')
// report.pdf

normalizePdfFilename('CON')
// _CON.pdf

normalizePdfFilename(' ग्राहक रिपोर्ट ')
// ग्राहक रिपोर्ट.pdf

Full Example

<script setup lang="ts">
import { ref } from 'vue'

import {
  Html2Pdf,
  type PdfMetadataOptions,
  type PdfProgressState,
  type PdfSecurityOptions,
  type WatermarkOptions,
} from 'vue-pdf-export'

import 'vue-pdf-export/style.css'

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

const security: PdfSecurityOptions = {
  enabled: true,
  userPassword: 'customer-1024',
  ownerPassword: 'owner-1024',
  permissions: ['print', 'copy'],
}

const metadata: PdfMetadataOptions = {
  title: 'Invoice #1024',
  author: 'Acme Inc.',
  subject: 'Customer invoice',
  keywords: ['invoice', 'customer'],
  creator: 'vue-pdf-export',
}

const watermark: WatermarkOptions = {
  enabled: true,
  type: 'text',
  layer: 'foreground',
  mode: 'repeat',
  text: 'CONFIDENTIAL',
  opacity: 0.08,
  rotation: -30,
  pages: 'except-first',
}

const htmlToPdfOptions = {
  enableLinks: true,
}

function onProgressStage(state: PdfProgressState) {
  console.log(state.stage, state.progress)
}

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

<template>
  <button type="button" @click="generatePdf">Generate PDF</button>

  <Html2Pdf
    ref="pdfRef"
    render-mode="text"
    filename="invoice-1024"
    :enable-download="true"
    :preview-modal="true"
    :manual-pagination="true"
    :inject-header="true"
    header-text="Acme Inc."
    :header-logo-enabled="true"
    header-logo-url="/logo.png"
    :header-logo-width="120"
    :header-logo-height="30"
    header-pages="first"
    :inject-footer="true"
    footer-mode="text"
    footer-text="Acme Inc."
    footer-template="Page {n} of {total}"
    :security="security"
    :metadata="metadata"
    :compress="true"
    :watermark="watermark"
    :html-to-pdf-options="htmlToPdfOptions"
    @progress-stage="onProgressStage"
  >
    <template #pdf-content>
      <main class="pdf-root">
        <section class="pdf-keep-together">
          <div class="vpe-reference-logo">Invoice #1024</div>
          <p>This text is selectable and searchable in text mode.</p>

          <a href="https://www.npmjs.com/package/vue-pdf-export"> View package on npm </a>
        </section>

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

        <section>
          <h2>Terms</h2>
          <p>Additional content...</p>
        </section>
      </main>
    </template>
  </Html2Pdf>
</template>

<style scoped>
.pdf-root {
  width: 794px;
  box-sizing: border-box;
}

.html2pdf__page-break {
  display: block;
  height: 0;
  clear: both;
}
</style>

Images & CORS

Remote images must be accessible to the browser during rendering.

Recommended:

html2canvas: {
  useCORS: true,
}

For external images:

  • configure appropriate CORS headers
  • avoid inaccessible authenticated image URLs
  • prefer data URLs for critical logos when practical
  • preserve image aspect ratio

Normal content image CSS:

.pdf-root img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

Header/footer logo sizing is already aspect-ratio-safe internally. Their width and height props define maximum bounds instead of forced image dimensions.

Production Guide

Keep styling disabled when it is not needed

styling.enabled can be used as a true feature switch. If styling is off, do not create styling-only work in the export path.

Keep the outer export width deterministic

A fixed outer PDF content width gives stable pagination. Internal page sections can use width: 100% with box-sizing: border-box so their padding stays inside the export width rather than increasing it.

Use a deterministic export width

A PDF should not unexpectedly reflow because it was generated from a narrow mobile viewport.

.pdf-root {
  width: 794px;
  box-sizing: border-box;
}

Match export width and pagination to orientation

pdfOrientation changes the PDF page orientation, but your own CSS pixel widths and automatic-pagination height still need to make sense for that orientation.

For deterministic layouts, keep one stable outer export width per orientation and let internal page sections use:

.pdf-page {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}

Avoid giving both the outer export root and an inner padded page the same fixed pixel width; the inner padding can otherwise make the visual box wider than the export root.

If your application switches between portrait and landscape, update any application-level pdfContentWidth / paginateElementsByHeight presets accordingly.

Keep export CSS separate from responsive application CSS

The web UI and the PDF document have different layout goals.

Choose the renderer intentionally

Use canvas when exact visual fidelity is the priority.

Use text when selectable/searchable/copyable PDF text is the priority and the document uses CSS supported by the vector renderer.

Avoid large fixed heights

Prefer min-height over large fixed height values where possible.

Protect important blocks

Use .pdf-keep-together, .pdf-no-break, or carefully chosen pagebreak.avoid selectors.

Preserve image aspect ratios

img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

Keep header/footer assets warm and reusable

Selectable-text headers/footers are drawn as PDF text/vector operations. Logo loading can still dominate the stage when an image is cold, remote, blocked by CORS, or slow to decode.

The renderer reuses a matching already-loaded browser image when possible. For consistently low latency:

  • prefer same-origin or data-URL logos
  • keep logo files small
  • preload critical logos in the application when appropriate
  • disable header/footer logos when they are not needed

Do not treat a specific header/footer millisecond value as a universal SLA; browser, device, image source, and cold-cache conditions can change it.

Keep header/footer content inside configured heights

Very large text or HTML content can overlap PDF content if it exceeds headerHeight or footerHeight.

Use trusted HTML only

headerHtml and footerHtml intentionally render developer-provided HTML.

Never pass unsanitized user-controlled content.

Avoid excessive letter spacing, transforms, or tightly fixed widths for PDF links/buttons when using selectable-text mode.

Use reasonable canvas quality

Higher html2canvas scale values consume more memory and CPU.

Generate one large document at a time

Multi-page PDF generation can be CPU and memory intensive.

Provide cancellation for expensive exports

Expose cancelGeneration() in long-running workflows so users can stop an export without waiting for the entire document to finish.

Cancellation is cooperative: already-running synchronous browser/jsPDF work finishes before the next cancellation checkpoint.

Test representative content

Test:

  • shortest document
  • longest document
  • missing fields
  • very long text
  • wide/square/tall logos
  • remote images
  • links
  • manual and automatic pagination
  • header/footer enabled and disabled
  • logo enabled and disabled
  • trusted HTML header/footer
  • selective header pages
  • watermark targeting
  • password protection
  • every permission combination important to your app
  • metadata
  • compression
  • both render modes

Performance & Large Documents

Large client-side PDF generation is constrained by browser memory, canvas limits, image size, document complexity, encryption overhead, and the device running the page. The package therefore uses multiple strategies rather than assuming one renderer is safe for every document.

Large-document architecture

The large-document architecture includes:

  • validation with 10 / 25 / 50 / 100-page representative documents
  • generation-time and PDF-size measurement
  • browser-memory experiments and repeated-generation testing
  • isolated Canvas/jsPDF analysis to distinguish package behavior from dependency behavior
  • page-by-page incremental Canvas rendering for paginated documents
  • temporary DOM cleanup after page capture
  • canvas backing-store release after raster insertion
  • selectable-text image materialization with per-generation repeated-image caching
  • cooperative browser yielding during expensive loops
  • cancellation and recovery testing
  • page-aware progress reporting
  • long-document Playwright stress/regression coverage
  • image-heavy, mixed-content, header/footer, watermark, metadata, compression, links, and security scenarios

Measured performance improvements

The performance work resulted in several measurable improvements in the benchmark fixtures:

AreaBeforeAfterImprovement
HTML footer without logo7.82 s1.43 s~81.7% less time
HTML footer with logo11.81 s1.29 s~89.1% less time
Footer-heavy PDF size~46.7 MB~1.0 MB~97.9% smaller
Footer no-logo peak heap102.6 MB26.5 MB~74.2% lower
Long Canvas capture surface~99.96M px~1.74M px per page~98.26% smaller peak capture geometry

These measurements come from controlled benchmark scenarios and should not be interpreted as a universal percentage improvement for every PDF.

The architectural changes behind these results include:

  • rendering static HTML footers once and reusing them across pages
  • page-by-page Canvas rendering for paginated documents
  • releasing temporary DOM and canvas backing stores earlier
  • caching repeated selectable-mode images within one generation
  • yielding between expensive rendering steps
  • adding cancellation and page-aware progress

Incremental Canvas strategy

For Canvas documents with .html2pdf__page-break markers, each logical page is rendered independently.

This avoids the old failure mode where a long document could become one extremely tall browser canvas. In regression testing, the incremental path restored correct 50-page and 100-page Canvas PDF output while keeping individual captures page-bounded.

The legacy whole-document html2pdf.js path is retained for Canvas documents that do not contain page-break markers.

Selectable-text image optimization

Selectable-text rendering uses a per-generation image cache for repeated real <img> sources when the source, target raster dimensions, format, and JPEG quality match.

Why the cache is intentionally per-generation:

  • avoids carrying stale DOM/image state across exports
  • keeps cache lifetime bounded
  • prevents a global cache from becoming a long-lived memory owner

Canvas elements are not cached because their pixels may change even when their dimensions do not.

Cleanup and browser responsiveness

Long exports explicitly release temporary resources where practical:

  • off-screen incremental page DOM is removed after html2canvas no longer needs it
  • temporary canvas backing stores are reset after serialization/insertion
  • selectable image-materialization canvases are released after encoding
  • per-generation image caches are cleared after use
  • rendering loops periodically yield with browser scheduling checkpoints

Yielding does not make CPU-heavy work free, but it gives the browser opportunities to process progress updates and cancellation rather than monopolizing the main thread for the entire export.

Cancellation and recovery

Both rendering modes support cooperative cancellation.

The regression suite checks that:

  1. an active long export can be cancelled,
  2. temporary rendering DOM is cleaned up,
  3. no successful Blob is exposed for the cancelled run, and
  4. a new PDF can be generated successfully afterwards.

Page progress

Page-aware renderers can report:

{
  stage: 'rendering',
  progress: number,
  currentPage?: number,
  totalPages?: number,
}

This is useful for long reports where a percentage alone does not explain what the renderer is doing.

Encrypted Canvas memory behavior

Large encrypted Canvas PDFs can retain substantially more memory than equivalent unencrypted Canvas PDFs in Chromium.

Isolation testing reproduced the severe encrypted-raster retention pattern with raw jsPDF usage, outside the Vue component lifecycle. For that reason this is tracked as an upstream/dependency concern rather than described as a confirmed Vue memory leak.

Practical guidance:

  • avoid unnecessarily high Canvas scale values
  • resize oversized images before PDF generation
  • generate one very large document at a time
  • prefer selectable-text mode when it meets the document's visual requirements
  • test encrypted long documents on representative target devices
  • consider splitting exceptionally large documents when browser memory is constrained

Upstream jsPDF tracking: large encrypted Canvas PDFs can show substantially higher memory retention than equivalent unencrypted Canvas PDFs in Chromium. The same encrypted-raster pattern was reproduced in a raw jsPDF-only test outside the Vue component lifecycle, so this is tracked as an upstream/dependency concern rather than described as a confirmed Vue memory leak. See jsPDF issue #4017.

Upstream tracking: jsPDF #4017 - encrypted raster PDF memory retention.

This link is included so developers using large encrypted Canvas PDFs can follow the dependency-level investigation directly.

Stress/regression coverage

AreaCoverage
Long Canvas documents10 / 25 / 50 / 100-page regression fixtures
Heavy cross-browser smokeChromium plus Firefox/WebKit coverage, with the heaviest 50/100-page cases concentrated in Chromium
Selectable-text long documentslong multi-page rendering and cancellation/recovery
Image-heavy selectable moderepeated image cache / raster-call regression
Image-heavy Canvas modepage-bounded canvas regression
CancellationCanvas and selectable-text cancellation + recovery
Cleanuptemporary page/snapshot/fixture cleanup assertions
Featuresheaders, footers, watermarks, links, metadata, compression, security

These results validate representative supported paths; they are not a universal guarantee that every arbitrary 100-page web document will fit every browser/device memory budget.

For large Canvas reports:

<Html2Pdf render-mode="canvas" :manual-pagination="true" :pdf-quality="1.5" />

Use explicit page markers:

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

For text-heavy reports where supported CSS is sufficient:

<Html2Pdf render-mode="text" />

Selectable-text mode avoids rasterizing the entire body and can be a better fit for long text-first documents.

About documents larger than 100 pages

The normal automated regression suite verifies large-document behavior through 100-page test fixtures.

The package has also been exercised with 5 / 10 / 25 / 50 / 100 / 200 / 500 / 1000-page stress scenarios. Higher page counts are workload experiments rather than a universal guarantee that every 1000-page document will succeed on every browser/device.

This does not mean the package has a hard 100-page limit. PDF generation does not stop at page 100, and larger documents may also work successfully.

However, documents beyond the tested range depend increasingly on the actual workload, including:

  • DOM size and complexity
  • number and dimensions of images
  • whether images are unique or repeatedly reused
  • renderer choice
  • browser and device memory
  • headers, footers, and watermarks
  • encryption and compression
  • final PDF serialization cost

For very large text-first documents, renderMode="text" is generally the preferred starting point when its supported CSS is sufficient, because body text is represented as PDF text/vector operations instead of full-page raster screenshots.

For large Canvas documents, use deterministic pagination/page-break markers so the incremental renderer can keep individual Canvas captures page-bounded.

The package also provides page-aware progress and cooperative cancellation so applications can give users meaningful feedback during long exports:

Rendering page 74 of 100

Security Model & Boundaries

vue-pdf-export provides PDF security features and conservative browser-side cleanup, but no client-side PDF package can make an application "100% secure" by itself.

Verified outcomes

The final regression suite verified:

  • 50-page Canvas generation
  • 100-page Canvas generation
  • Canvas cancellation and recovery
  • selectable-text cancellation and recovery
  • repeated-image caching in selectable-text mode
  • page-bounded Canvas rendering in image-heavy documents
  • temporary rendering cleanup after cancellation/generation

The final unit suite completed with:

35 test files passed
149 / 149 tests passed

What the package provides

  • jsPDF-backed PDF encryption/password protection
  • separate user/open and owner password inputs
  • supported permission flags for print, modify, copy, and annotation/forms
  • explicit security.enabled: false handling to remove inherited raw encryption
  • metadata/compression processing without sending the document to a package-operated backend
  • Blob URL cleanup in the component/composable lifecycle
  • cancellation cleanup for temporary rendering structures
  • filename normalization that removes unsafe path/filename characters and protects Windows reserved names

What remains the application's responsibility

  • sanitizing any untrusted HTML before passing it to headerHtml or footerHtml
  • protecting passwords, tokens, customer data, and remote asset URLs
  • server-side authorization before sensitive data reaches the browser
  • selecting CORS-safe and trusted image origins
  • deciding whether client-side PDF generation is appropriate for highly sensitive documents
  • validating the behavior of permission flags in the PDF viewers used by your customers

Important client-side security note

Anything available to frontend JavaScript can potentially be inspected by a user who controls that browser session. Do not treat a frontend bundle, minification, or obfuscation as a secret-storage mechanism.

Minifying the npm distribution is useful for package size and distribution hygiene, but it is not a security boundary.

Selectable Text Mode Notes

Selectable-text mode uses a browser-layout-to-vector-PDF renderer rather than html2canvas for the main body content.

What it is designed to preserve

  • real PDF text
  • selection/search/copy
  • text line placement based on browser layout
  • common font weight/style/color/alignment
  • backgrounds
  • borders
  • images
  • opacity
  • overflow clipping
  • multi-page layout
  • clickable links

Current limitations

Some browser CSS/features are not fully reproduced in selectable-text mode, including or depending on renderer support:

  • complex stacking-context / z-index reordering
  • background images and gradients
  • shadows
  • CSS transforms
  • custom TTF/web-font embedding
  • some non-WinAnsi glyphs such as CJK/emoji
  • pseudo-elements such as ::before / ::after
  • native list markers in some layouts
  • RTL text
  • position: fixed
  • complex rounded clipping
  • native break-inside: avoid semantics inside the vector renderer

For those cases, prefer renderMode="canvas".

Font metric differences

Selectable text uses PDF font metrics, which can differ slightly from browser web-font metrics. Most normal text is visually close, but highly stylized button/link text with large letter spacing or fixed widths can look different.

Header/footer text

In selectable-text mode, plain header/footer text and footer page-number text are emitted as real selectable PDF text. Complex HTML styling may be simplified to preserve selectability.

Security

Selectable-text mode supports PDF encryption because the vendored vector renderer passes the package's jsPDF constructor options at document creation time.

Troubleshooting

Selectable text is not pixel-identical to the browser

Use render-mode="canvas" for maximum CSS fidelity.

Selectable-text mode intentionally prioritizes real PDF text and vector output.

Make sure links are enabled:

const htmlToPdfOptions = {
  enableLinks: true,
}

Also verify the source element is a real anchor with a valid href.

Reduce large letter-spacing, transforms, and tight fixed widths in selectable-text mode.

Logo looks stretched

Header/footer logo dimensions are maximum bounds and preserve the source aspect ratio.

For normal content images:

img {
  width: auto;
  height: auto;
  max-width: 100%;
  object-fit: contain;
}

Header/footer overlaps content

Increase headerHeight / footerHeight, or reduce content size.

For selectively targeted headers, only target pages receive the extra header space. If excluded pages still have top spacing, check explicit htmlToPdfOptions.margin.

Check:

  • URL accessibility
  • CORS
  • data URL validity
  • headerLogoEnabled
  • footerLogoEnabled

HTML header/footer is not visible

Check that:

  • related header/footer is enabled
  • HTML string is non-empty
  • footer uses footer-mode="html"
  • content fits inside configured height

Blank page around a manual break

Use only:

.html2pdf__page-break {
  display: block;
  height: 0;
  clear: both;
}

Do not add break-before or page-break-before to the same helper.

Background watermark is hidden

Opaque PDF content can cover a DOM background watermark.

Use a foreground watermark or transparent export content where appropriate.

PDF is blurry

In canvas mode, increase pdfQuality or htmlToPdfOptions.html2canvas.scale carefully.

Selectable-text body text remains vector text and does not depend on html2canvas resolution.

Landscape output is clipped or shifted

Check application-level export sizing in addition to pdfOrientation.

  • keep the outer export width deterministic for the selected orientation
  • use width: 100% + box-sizing: border-box for padded internal page sections
  • avoid reusing a portrait-only automatic-pagination height in landscape
  • verify header/footer reserved space against the shorter landscape page height

Header/footer stage is unexpectedly slow

If selectable-text header/footer timing is dominated by a logo, check whether the image is cold, remote, CORS-blocked, or failing to decode. Same-origin/data-URL assets and already-loaded images avoid unnecessary image-loading work.

A cold remote asset can still exceed 100ms; timing depends on the browser/device/network and should be measured rather than treated as a hard guarantee.

Large document generation is slow

Reduce:

  • canvas scale where relevant
  • image dimensions
  • document complexity
  • simultaneous PDF generation

Large encrypted Canvas PDFs can show substantially higher memory retention than unencrypted Canvas PDFs in Chromium. Raw jsPDF-only isolation reproduced the same encrypted-raster retention pattern outside the Vue lifecycle, so this is tracked as an upstream/dependency concern rather than described as a confirmed permanent Vue memory leak. Selectable-text mode did not show the same severe raster-memory pattern in the package benchmarks.

SSR / Nuxt

Importing vue-pdf-export during SSR is supported. PDF generation itself must run in a browser environment.

SSR/runtime compatibility has been validated for:

  • Nuxt 3
  • Nuxt 4
  • server-side package import
  • hydration
  • client navigation
  • lazy-loaded PDF consumers
  • <ClientOnly> integration
  • client-event composable generation

Browser-heavy PDF dependencies are lazy-loaded from the generation paths rather than being eagerly evaluated at SSR import time.

Browser Support

The package targets modern browsers supported by vue and the underlying PDF/browser APIs.

Recommended testing:

  • Chromium / Chrome / Edge
  • Firefox
  • WebKit
  • Safari when Safari is important to production users

Playwright WebKit tests the WebKit engine; it is not identical to testing the Safari application itself.

Quality & Compatibility

vue-pdf-export is validated across the package's major public workflows, including:

  • Canvas and selectable-text PDF generation
  • preview, download, and native browser print flows
  • manual and automatic pagination
  • data-pdf-ignore
  • export-only page/body styling
  • page/body backgrounds, classes, inline styles, per-page styling, custom CSS, themes, and auto-fit
  • clickable PDF links
  • headers, footers, logos, and page numbering
  • text and image watermarks
  • metadata and compression
  • password protection and PDF permission flags
  • progress stages, page-aware progress, and cooperative cancellation
  • generation metrics
  • repeated-image optimization
  • large-document and memory-sensitive rendering paths
  • portrait and landscape layouts
  • SSR-safe imports
  • Nuxt 3 and Nuxt 4 integration
  • packed-package consumer validation with npm and pnpm
  • public output API/source checks for Blob, ArrayBuffer, and Base64 support
  • Chromium, Firefox, and WebKit browser engines

Large-document behavior depends on document complexity, image sizes, renderer choice, browser, device memory, encryption, and PDF options. Treat the documented stress results as engineering validation rather than a fixed page-count guarantee.

For application-specific production use, test representative documents in the browsers and devices that matter to your users.

TypeScript

The package ships TypeScript declarations.

Common types:

import type {
  BeforeDownloadPayload,
  FooterMode,
  FooterOptions,
  GeneratePdfResult,
  HeaderOptions,
  Html2CanvasOptions,
  Html2PdfOptions,
  Html2PdfProps,
  JsPdfOptions,
  PdfEncryptionOptions,
  PdfFormat,
  PdfImageOptions,
  PdfMargin,
  PdfMetadataOptions,
  PdfOrientation,
  PdfOutputFormat,
  PdfBase64Options,
  PdfSerializedOutput,
  PdfPageBreakOptions,
  PdfPageTarget,
  PdfPermission,
  PdfProgressStage,
  PdfProgressState,
  PdfRenderMode,
  PdfSecurityOptions,
  WatermarkLayer,
  WatermarkMode,
  WatermarkOptions,
  WatermarkType,
} from 'vue-pdf-export'

Render mode type:

export type PdfRenderMode = 'canvas' | 'text'

Useful helpers:

import {
  createPdfOptions,
  normalizePdfFilename,
  resolvePdfPages,
  pdfBlobToArrayBuffer,
  pdfBlobToBase64,
  serializePdfOutput,
} from 'vue-pdf-export'

Styling types

import type {
  PdfAutoFitOptions,
  PdfForcedTheme,
  PdfPageContext,
  PdfStyle,
  PdfStyleResolver,
  PdfStylingOptions,
} from 'vue-pdf-export'

PdfStylingOptions is the preferred object API for export-only PDF styling.

Community

The package source is currently maintained privately. This README is therefore written as consumer documentation for the published npm package rather than as a source-repository contribution guide.

You do not need repository access to install or use vue-pdf-export.

Feedback and feature requests are welcome.

Acknowledgements

Selectable-text rendering in vue-pdf-export is based on and adapted from @timkozlov/html-to-pdf, created by @Canelex and released under the MIT License.

A big thank you to the author for the browser DOM-to-vector-PDF work that made real selectable/searchable text practical in this package.

Upstream project: Canelex/html-to-pdf.

vue-pdf-export vendors and adapts that renderer for integration with its own API, including areas such as jsPDF constructor options/security, existing page-break compatibility, shared PDF post-processing, and package-specific behavior. The original license notice is retained with the vendored source.

License

MIT © 2026 Saurabh Choudhary

Third-party vendored code remains subject to its original copyright and MIT license notices.

Support the project

If vue-pdf-export is useful in your project, you can support its continued development:

Support is optional. Bug reports, feedback, and feature requests are also welcome through the project feedback board.

Copyright © 2026