DEV Community

Cover image for Building a Better PDF Generator in Vue 3 with vue-pdf-export
Saurabh Choudhary
Saurabh Choudhary

Posted on AI-assisted

Building a Better PDF Generator in Vue 3 with vue-pdf-export

Hey Vue developers đź‘‹

I’ve been building vue-pdf-export, an open-source Vue 3 + TypeScript library for generating PDFs directly in the browser.

I started this project because I kept rebuilding the same PDF features across Vue applications: pagination, preview, printing, headers, footers, progress tracking, and long-document support. I wanted a reusable component and composable rather than repeating that infrastructure in every project.

In short: Generate PDFs from Vue 3 components in the browser using either Canvas rendering or selectable, searchable text. The package also supports smart pagination, tables, headers, footers, watermarks, preview, download, print, and SSR-safe imports for Nuxt 3 and Nuxt 4.

You can explore the actual PDF output without setting up a project:


Here is what the published package supports:

In this guide

Quick answer - How do I generate a PDF in Vue 3? Install vue-pdf-export, import Html2Pdf and its stylesheet, place your document in the #pdf-content slot, and call generatePdf() from a Vue template ref. Use render-mode="text" for native searchable/selectable body text; choose render-mode="canvas" when complex browser CSS fidelity matters most. Both modes generate PDFs in the browser.

Features

  • Powerful: choose Canvas or selectable-text output, use your own Vue document components, and control page breaks.
  • Modular: use the component or composable, and add only the slots and options your document needs. PDF engines load when generation runs.
  • Customizable: style exported content with your CSS and export-only options; theme the built-in preview and loader with CSS variables.
  • Canvas and selectable-text PDF rendering
  • Smart automatic/manual pagination, keep-together blocks and container-aware splitting
  • Explicit page breaks, repeated table headers and opt-in pagination diagnostics
  • Preview, download, and native browser print
  • Blob, ArrayBuffer, Base64, and PDF data-URL output
  • Header/footer object options, Vue slots and page targeting
  • PDF composition: prepend or append existing PDF pages
  • Standard paper formats and custom dimensions in portrait or landscape
  • Text, image and trusted HTML watermarks with optional design presets; text marks use reusable vector outlines instead of selectable PDF text
  • Password protection, permissions, metadata, and compression
  • Clickable PDF links
  • Progress reporting, generation metrics, and cancellation
  • data-pdf-ignore for export-only DOM control
  • PDF-only page/body styling and custom CSS
  • Large-document rendering optimizations
  • Font readiness, optional TTF/TrueType-outline OTF embedding, chart snapshots and SVG fallback
  • Opt-in image optimization with explicit quality controls and per-export asset reuse metrics
  • Nuxt 3 and Nuxt 4 SSR-safe imports
  • TypeScript declarations
  • npm, pnpm, Yarn, Yarn PnP, and Bun consumer validation

For detailed API options, examples, renderer notes, styling, pagination,
security, SSR guidance, and production recommendations, see the
full documentation.

Security note: Header, footer, and slot HTML is sanitized before entering export staging. Treat caller-provided HTML as untrusted and apply your application’s input validation policy.

Privacy and network boundary: Ordinary PDF generation happens in your browser and does not upload document content to a package-operated server. URL-based templates, remote fonts/images, and external export stylesheets may still be fetched from their configured origins.


Install

npm install vue-pdf-export
Enter fullscreen mode Exit fullscreen mode

Small example

<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);
</script>

<template>
  <button @click="pdfRef?.generatePdf()">Generate PDF</button>

  <Html2Pdf ref="pdfRef" filename="report.pdf">
    <template #pdf-content>
      <h1>Hello from Vue</h1>
      <p>This content will be exported to PDF.</p>
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

Try it

Playground:

https://vue-pdf-export-playground.vercel.app/

Documentation:

https://vue-pdf-export-docs.vercel.app/

npm:

https://www.npmjs.com/package/vue-pdf-export

I’d really like feedback from Vue developers who have worked with PDFs in production - especially around pagination, large documents, selectable text, and API ergonomics.


Complete Vue 3 PDF generation guide

Scope: This guide describes the currently published vue-pdf-export functionality and combines practical examples with API detail. Code samples and recorded benchmarks reflect the documented test scenarios; run your own target-browser tests before shipping production PDFs. For the maintained source of truth, use the official documentation and GitHub repository.

This complete technical guide covers installation, JavaScript and TypeScript usage, components and composables, rendering modes, pagination, styling, page layers, programmatic output, performance, testing, production checks, and browser limitations. It also links to the detailed performance engineering report for reproducible measurements.

Get Started

Introduction

vue-pdf-export is a Vue 3 HTML-to-PDF component and composable for browser applications. Use it from JavaScript or TypeScript.

The API includes smart split controls, row-aware tables, header/footer objects and Vue slots, PDF composition, expanded paper sizes, font readiness, chart snapshots, SVG fallback, and opt-in image optimization.

It 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, native browser print, Blob/ArrayBuffer/Base64 output, 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, opt-in generation metrics, current-page progress, cancellation, page targeting, PDF permissions, SSR-safe imports, large-document optimizations, and advanced renderer configuration.

Default rendering mode: renderMode="canvas". The live playground may select another 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
  • Native browser print support in both rendering modes
  • Incremental page-by-page Canvas rendering when page-break markers are present
  • Cooperative cancellation for long-running exports
  • Page-aware progress with currentPage / totalPages
  • Opt-in total, stage, and per-page generation timing metrics
  • data-pdf-ignore for screen-only UI that must stay out of generated PDFs
  • Export-only page/body styling without mutating the visible Vue DOM
  • Repeated-image caching in selectable-text rendering
  • Optional TTF/TrueType-outline OTF embedding for selectable text
  • Export-only chart snapshots, SVG fallback and explicit image quality controls
  • Text, image and trusted HTML watermark designs with per-export reuse
  • 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
  • SSR-safe package imports with Nuxt 3 and Nuxt 4 validation
  • Packed-consumer verification across npm, pnpm, Yarn, Yarn Plug'n'Play, and Bun
  • Large-document stress presets from 5 through 1000 pages, plus custom counts

Presets are test inputs, not a guarantee that every page count will succeed on every device. Test representative documents in your target browsers.

Try it before integrating: The live playground exposes package options, output APIs, and diagnostics interactively.

Open the interactive PDF playground
Open playground


Installation

Install the package using your preferred package manager:

npm install vue-pdf-export
# or
pnpm add vue-pdf-export
# or
yarn add vue-pdf-export
# or
bun add vue-pdf-export
Enter fullscreen mode Exit fullscreen mode

Import

TypeScript:

import {
  Html2Pdf,
  type PdfBase64Options,
  type PdfMetadataOptions,
  type PdfOutputFormat,
  type PdfProgressState,
  type PdfRenderMode,
  type PdfSecurityOptions,
  type PdfSerializedOutput,
  type PdfStylingOptions,
  type WatermarkOptions,
} from "vue-pdf-export";

import "vue-pdf-export/style.css";
Enter fullscreen mode Exit fullscreen mode

JavaScript:

import { Html2Pdf } from "vue-pdf-export";
import "vue-pdf-export/style.css";
Enter fullscreen mode Exit fullscreen mode

Package-manager compatibility

The packed package is validated as a consumer installation with:

  • npm
  • pnpm
  • Yarn
  • Yarn Plug'n'Play
  • Bun

The compatibility fixtures verify installation, TypeScript resolution, production builds, CSS imports, and browser-side PDF generation.

Nuxt

vue-pdf-export can be imported during SSR, while PDF generation itself remains browser-only.

See Nuxt 3 & Nuxt 4 SSR for <ClientOnly>, lazy-import, and client-event patterns.


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>
        <h1>My Report</h1>
        <p>This content will be rendered into the PDF.</p>
      </div>
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

Plain JavaScript with the Vue Options API

TypeScript is optional. Register Html2Pdf locally and call generatePdf() through a template ref. PDF generation runs in the browser after the component mounts.

<script>
import { Html2Pdf } from "vue-pdf-export";
import "vue-pdf-export/style.css";

export default {
  components: { Html2Pdf },
  methods: {
    async generate() {
      const blob = await this.$refs.pdf.generatePdf();
      console.log("PDF bytes:", blob.size);
    },
  },
};
</script>

<template>
  <button type="button" @click="generate">Generate PDF</button>
  <Html2Pdf ref="pdf" :enable-download="false">
    <template #pdf-content
      ><article><h1>JavaScript report</h1></article></template
    >
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

Without a pagination height, the renderer handles basic page flow. Set a positive paginateElementsByHeight when you want the smart DOM planner; see pagination. The Options API example is based on the library's npm consumer fixture.


Rendering Modes

Canvas mode

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

<Html2Pdf render-mode="canvas" manual-pagination />
Enter fullscreen mode Exit fullscreen mode

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

Canvas keeps inline SVG in its export staging by default, but the final PDF page is still a raster image. Chart canvases are snapshotted on the private export clone before capture. See fonts, charts, SVG and images.

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.

A viewer such as Chrome may apply OCR and let users select apparent text in an image-only PDF. A foreground text watermark or some numbering paths can also add native text over a Canvas body. Inspect PDF text operators or extract text with a PDF tool to distinguish native text from viewer OCR.

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 use the whole-document html2pdf.js worker. Paginated Canvas documents can use page-bounded incremental captures.

Oversized splittable Canvas sections are measured and re-paginated on private staging DOM before capture. Reused fitting fragments and page-by-page capture reduce repeated work; very large indivisible media can still scale or split. Vue header/footer slots use image captures in Canvas mode and supported text/shape operations in text mode.

Selectable text mode

<Html2Pdf render-mode="text" manual-pagination />
Enter fullscreen mode Exit fullscreen mode

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

In selectable-text mode, inline SVG shapes are rasterized; svgMode="preserve" cannot turn them into native PDF vectors. Optional TTF or TrueType-outline OTF registration can embed a matching face for real PDF text. Without a registered font, unrecognized CSS families use built-in PDF faces.

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" manual-pagination>

  <template #pdf-content>
    <main>
      <h1>Selectable report</h1>
      <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>
Enter fullscreen mode Exit fullscreen mode

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


Component Registration

Local import - recommended

With <script setup>:

<script setup lang="ts">
import { Html2Pdf } from "vue-pdf-export";
import "vue-pdf-export/style.css";
</script>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

Preview & Download

Add one of these prop combinations to the complete quick-start component. Keep manual-pagination for a simple document, or configure a usable page-height budget for automatic pagination.

Preview only:

<Html2Pdf :enable-download="false" :preview-modal="true" />
Enter fullscreen mode Exit fullscreen mode

Download only:

<Html2Pdf :enable-download="true" :preview-modal="false" />
Enter fullscreen mode Exit fullscreen mode

Download and preview:

<Html2Pdf :enable-download="true" :preview-modal="true" />
Enter fullscreen mode Exit fullscreen mode

enableDownload and previewModal are independent.


Smart PDF Pagination

Html2Pdf runs the shared DOM pagination planner when manualPagination is false and paginateElementsByHeight is supplied. That number is the usable CSS pixel height after margins and header/footer reservations, calculated for your chosen paper size and orientation. It is not a universal A4 constant. Direct useHtml2Pdf() generation does not run the component planner.

The component also supports a simple Options API or Composition API PDF without pagination props: the selected renderer handles basic flow. Set a positive finite height to use the smart DOM planner for keep-together rules and table rows. In the playground, manual pagination is the demo default, and the optional media/chart section has an authored break before it. A large section may continue onto another physical page. Oversized rows and unusual CSS still need PDF inspection.

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

// Example budget for this application's measured paper geometry and insets.
const usablePageHeightPx = 960;
</script>

<template>
  <Html2Pdf
    :manual-pagination="false"
    :paginate-elements-by-height="usablePageHeightPx"
  >
    <template #pdf-content>
      <section data-pdf-split="avoid">
        <h2>Summary</h2>
        <p>Keep this block together if it fits.</p>
      </section>
      <section data-pdf-split="container">
        <header data-pdf-split-header>Repeated section heading</header>
        <article>First child</article>
        <article>Second child</article>
      </section>
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

Recalculate the budget when format, orientation, explicit margins, content width or layer heights change. The paper format guide explains geometry.

Split controls

Markup Effect
data-pdf-split="avoid" Move a fitting block together; oversized blocks can fall back to child boundaries.
data-pdf-split="container" Split long and nested sections around children, accounting for computed padding and flex/grid gaps.
data-pdf-split-header Mark a child heading for repetition when its container continues.
.pdf-keep-together, .pdf-no-break, img Existing default avoidance rules.

In supported selectable-text layouts, continuation fragments can retain a frame with page-edge spacing and rounded borders. Renderer CSS support varies. Indivisible oversized media, rowspans and complex nested layouts may still split or scale; inspect representative exports.

Explicit page breaks

<section style="break-before: page">Start on a new page.</section>
<section style="break-after: page">
  The next block starts on a new page.
</section>
<div class="html2pdf__page-break"></div>
Enter fullscreen mode Exit fullscreen mode

CSS page-break-before and page-break-after are supported alongside modern break properties. Do not put an explicit CSS break on the .html2pdf__page-break marker: that can produce two breaks or blank pages. manual-pagination="true" is useful when you author your own markers. Elements without special attributes retain their existing behavior.

Long tables

Use one semantic heading and ordinary rows. The planner can split across multiple <tbody> groups, honor explicit row breaks, move an orphan heading with its first row and repeat <thead> on continuation pages. Do not manually copy the heading.

<template>
  <table>
    <thead>
      <tr>
        <th scope="col">Item</th>
        <th scope="col">Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in 40" :key="item">
        <td>Item {{ item }}</td>
        <td>${{ item * 10 }}</td>
      </tr>
    </tbody>
  </table>
</template>
Enter fullscreen mode Exit fullscreen mode

Selectable-text rendering carries the computed heading background into repeated rows. Canvas continuation fragments suppress a second native heading repetition. Check complex rowspan, unsupported CSS and oversized rows in the output.

Diagnose page boundaries

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

function onBreak(decision: PdfPaginationDiagnostic) {
  console.info(decision.type, decision.page, decision.reason);
}
</script>

<template>
  <Html2Pdf
    :manual-pagination="false"
    :paginate-elements-by-height="960"
    :pagination-debug="true"
    @pagination-break="onBreak"
    @pagination-warning="onBreak"
    @pagination-complete="(all) => console.info(all)"
  >
    <template #pdf-content><article>Measured report content</article></template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

Possible diagnostic types are automatic, manual-break, break-before, break-after, avoid-moved, container-split, table-split, and oversized. The opt-in overlay and events show planner decisions on the live DOM. The overlay is not a PDF layer or proof of final viewer alignment. Pagination has a finite fallback and cancellation checks; it cannot guarantee that arbitrary text, SVG or CSS never tears.


Nuxt 3 & Nuxt 4 SSR

Nuxt logo

Nuxt 3 + Nuxt 4 compatible

vue-pdf-export supports SSR-safe package imports in both Nuxt 3 and Nuxt 4. PDF generation itself uses browser APIs and must run on the client.

Validated integration paths include:

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

Browser-heavy PDF dependencies are loaded from generation paths instead of being eagerly evaluated during the server import path.

Recommended component pattern

Use <ClientOnly> when the export UI itself does not need to render on the server:

<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 exportPdf() {
  await pdfRef.value?.generatePdf();
}
</script>

<template>
  <ClientOnly>
    <Html2Pdf
      ref="pdfRef"
      filename="nuxt-report.pdf"
      :enable-download="true"
      manual-pagination
    >
      <template #pdf-content>
        <article>
          <h1>Nuxt report</h1>
          <p>This PDF is generated in the browser.</p>
        </article>
      </template>
    </Html2Pdf>

    <button @click="exportPdf">Export PDF</button>
  </ClientOnly>
</template>
Enter fullscreen mode Exit fullscreen mode

Lazy import on a client event

For routes where PDF functionality is only used after a user action, you can lazy-load the package from a client event:

async function loadPdfTools() {
  if (!import.meta.client) return;

  const pdf = await import("vue-pdf-export");

  return pdf;
}
Enter fullscreen mode Exit fullscreen mode

This is optional. A normal package import is SSR-safe, but lazy loading can help keep PDF-specific code out of paths that never use it.

Composable generation

Call browser-side generation from mounted/client UI events rather than server setup work.

import { useHtml2Pdf } from "vue-pdf-export";

const { generate, loading, error } = useHtml2Pdf();

async function onExportClick() {
  if (!import.meta.client) return;

  await generate(/* browser DOM/content options */);
}
Enter fullscreen mode Exit fullscreen mode

What not to do

Do not start PDF generation while Nuxt is rendering on the server:

// Avoid server-side generation calls.
if (import.meta.server) {
  // generatePdf() should not be called here.
}
Enter fullscreen mode Exit fullscreen mode

The supported boundary is simple: imports may happen during SSR; PDF generation happens in the browser.

Nuxt compatibility: Nuxt 3 and Nuxt 4 have automated SSR import/hydration fixture coverage. Package import is SSR-safe; actual PDF generation must run in the browser.



PDF Paper Formats and Orientation

The component accepts a named pdf-format string and pdf-orientation="portrait|landscape" (also p|l). Default format is a4, default orientation is portrait.

<Html2Pdf pdf-format="a3" pdf-orientation="landscape" :manual-pagination="true">
  <template #pdf-content><article>Landscape document</article></template>
</Html2Pdf>
Enter fullscreen mode Exit fullscreen mode

The source normalizes A0–A10, B0–B10, C0–C10, dl, letter, government-letter, legal, junior-legal, ledger, tabloid, and credit-card. The selectable-text adapter and playground picker use these names. Small paper needs compact content; large or complex layouts still need viewer checks.

For custom dimensions, pass a numeric pair through the lower-level jsPDF options. The component's pdfFormat prop is a string, so do not bind an array to pdf-format.

<Html2Pdf
  :manual-pagination="true"
  :html-to-pdf-options="{
    jsPDF: { unit: 'mm', format: [100, 180], orientation: 'portrait' },
  }"
>
  <template #pdf-content><article>Custom-size document</article></template>
</Html2Pdf>
Enter fullscreen mode Exit fullscreen mode

When using automatic component pagination, calculate paginate-elements-by-height as the usable CSS pixel budget for the effective format, orientation, margins and header/footer heights. The playground computes this for its selected geometry. The selectable-text A3 fractional-boundary loop fix prevents a former repeated-page hang; final visual acceptance for all combinations still needs browser inspection.


API

Html2Pdf Component

Core props

Prop Type Default Description
renderMode `'canvas' \ 'text'` 'canvas'
showLayout boolean false Show export layout.
floatLayout boolean false Use fixed/floating export layout.
enableDownload boolean true Download after generation.
previewModal boolean false Show PDF preview modal.
filename string timestamp fallback PDF filename.
pdfQuality number 2 Default html2canvas scale; mainly relevant to canvas mode/images.
pdfFormat PDF format 'a4' PDF format.
pdfOrientation `'portrait' \ 'landscape'` 'portrait'
pdfContentWidth string '800px' Export wrapper width.
htmlToPdfOptions Html2PdfOptions package defaults Advanced options.
metadata PdfMetadataOptions undefined PDF metadata.
compress boolean undefined jsPDF stream compression override.
security PdfSecurityOptions undefined Password/security configuration.
manualPagination boolean false Manual pagination mode.
paginateElementsByHeight number undefined Positive finite height enables smart DOM pagination; otherwise the renderer handles basic page flow.
showLoader boolean true Show loader.
loaderText string 'Generating PDF...' Loader text.
watermark WatermarkOptions disabled Watermark configuration.
collectMetrics boolean false Collect total/stage/per-page generation timing metrics.
styling PdfStylingOptions disabled/defaults Apply export-only page/body styling, scoped CSS, theme and auto-fit controls.

Header and footer props are documented in their dedicated sections.

Additional component options include header?: HeaderOptions, footer?: FooterOptions, composition?: PdfCompositionOptions, paginationDebug?: boolean, pdfTemplate?: PdfTemplateSource, and templatePosition?: "prepend" | "append" | "both". See headers, footers, composition, and pagination.

The component also accepts fonts, autoOptimize, and svgMode. See the media and typography guide.

Exposed methods

interface Html2PdfInstance {
  generatePdf: (overrides?: {
    composition?: PdfCompositionOptions;
  }) => Promise<Blob>;
  generatePdfArrayBuffer: () => Promise<ArrayBuffer>;
  generatePdfBase64: (options?: PdfBase64Options) => Promise<string>;
  print: () => Promise<void>;
  closePreview: () => void;
  resetPagination: () => void;
  cancelGeneration: () => void;
}
Enter fullscreen mode Exit fullscreen mode

generatePdfArrayBuffer() and generatePdfBase64() reuse the generated PDF Blob and do not run a second renderer.

See Programmatic API for output examples and cancellation details.


Core Props

Prop Type Default Description
renderMode `'canvas' \ 'text'` 'canvas'
showLayout boolean false Show export layout.
floatLayout boolean false Use fixed/floating export layout.
enableDownload boolean true Download after generation.
previewModal boolean false Show PDF preview modal.
filename string timestamp fallback PDF filename.
pdfQuality number 2 Default html2canvas scale; mainly relevant to canvas mode/images.
pdfFormat PDF format 'a4' PDF format.
pdfOrientation `'portrait' \ 'landscape'` 'portrait'
pdfContentWidth string '800px' Export wrapper width.
htmlToPdfOptions Html2PdfOptions package defaults Advanced options.
metadata PdfMetadataOptions undefined PDF metadata.
compress boolean undefined jsPDF stream compression override.
security PdfSecurityOptions undefined Password/security configuration.
manualPagination boolean false Manual pagination mode.
paginateElementsByHeight number undefined Positive finite height activates the smart DOM pagination planner; otherwise the renderer handles basic page flow.
showLoader boolean true Show loader.
loaderText string 'Generating PDF...' Loader text.
watermark WatermarkOptions disabled Watermark configuration.
collectMetrics boolean false Collect total, stage, and per-page generation metrics.
fonts PdfFontSource[] undefined Register TTF/TrueType-outline OTF faces for selectable text.
autoOptimize `boolean \ PdfImageOptimizationOptions` disabled
svgMode `'auto' \ 'preserve' \ 'rasterize'`
styling PdfStylingOptions disabled/defaults Export-only page/body styles, scoped CSS, external stylesheets, theme and auto-fit controls.

Header and footer props are documented in their dedicated sections.

See fonts, charts, SVG and images for examples and limitations of the media options above.

Additional prop Type Purpose
header HeaderOptions Built-in header or slot layout and page targeting.
footer FooterOptions Built-in footer or slot layout.
composition PdfCompositionOptions Prepend and/or append existing PDF pages.
pdfTemplate, templatePosition PdfTemplateSource, `'prepend' 'append'
{% raw %}paginationDebug boolean, default false Live planner overlay and diagnostic events.

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,
  },
};
Enter fullscreen mode Exit fullscreen mode
<Html2Pdf :html-to-pdf-options="htmlToPdfOptions" />
Enter fullscreen mode Exit fullscreen mode

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.

PDF Headers in Vue

Set enabled: true for a built-in header:

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

const header: HeaderOptions = {
  enabled: true,
  height: 58,
  pages: "all",
  text: "Quarterly report",
  showLogo: true,
  logoUrl: "/logo.png",
  logoWidth: 120,
  logoHeight: 32,
  backgroundColor: "#F5FBF9",
  textColor: "#17324A",
};
</script>

<template>
  <Html2Pdf :header="header" :manual-pagination="true">
    <template #pdf-content><article>Document content</article></template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode
Field Default Meaning
enabled false Render the header.
height 58 via component Reserved height in the active jsPDF unit (px by default).
pages 'all' 'all', 'first', 'last', 'except-first', or a 1-based page number array.
text / html empty Plain text or trusted HTML; non-empty HTML takes precedence.
showLogo true Show an available logo.
logoUrl / logoDataUrl empty Logo URL or data URL.
logoWidth / logoHeight 28 CSS px Maximum logo box, preserving aspect ratio.
backgroundColor / textColor #F8F9FA / #5F6368 Built-in layer colors.

Header height reserves space only on targeted generated pages. Explicit top margins still apply. Sanitize user-authored HTML before assigning it to html.

Vue header slot

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

<template>
  <Html2Pdf
    :header="{ height: 58, pages: 'except-first' }"
    :manual-pagination="true"
  >
    <template #pdf-content><article>Report body</article></template>
    <template #pdf-header="{ pageNumber, totalPages, orientation }">
      <ReportHeader
        :page-number="pageNumber"
        :total-pages="totalPages"
        :orientation="orientation"
      />
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

For example, ReportHeader.vue can keep its own logo and scoped styles:

<script setup lang="ts">
defineProps<{ pageNumber: number; totalPages: number; orientation: string }>();
</script>

<template>
  <div class="report-header">
    <img src="/logo.png" alt="Company" width="100" height="28" />
    <strong>Quarterly report</strong>
    <small>{{ orientation }} · {{ pageNumber }}/{{ totalPages }}</small>
  </div>
</template>

<style scoped>
.report-header {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 10px 20px;
  background: #f5fbf9;
  color: #17324a;
}
.report-header small {
  margin-left: auto;
}
</style>
Enter fullscreen mode Exit fullscreen mode

The slot activates the header and owns visible markup. Object text, HTML, logo and colors are not injected into it; object height and pages still control layout and targeting. Give the component root width: 100% and appropriate padding. Scoped and inherited computed styles are snapshotted, subject to renderer support. Text mode emits supported text and shapes as PDF operations; Canvas mode captures an image. Inspect complex CSS and fonts in a viewer.

See footers and pagination for related configuration.


PDF Footers in Vue

The built-in footer appears on every generated page. Footers have no separate pages selector.

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

const footer: FooterOptions = {
  enabled: true,
  height: 58,
  useHtml: true,
  html: "<strong>My company</strong>",
  template: "Page {n} of {total}",
};
</script>

<template>
  <Html2Pdf :footer="footer" :manual-pagination="true">
    <template #pdf-content><article>Document content</article></template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode
Field Default Meaning
enabled false Render the footer.
height 58 via component Reserved height in the active jsPDF unit (px by default).
useHtml component compatibility default true HTML/image or plain-text built-in footer.
template Page {n} of {total} Generated-page number text.
text / html empty Body content; non-empty HTML takes precedence in HTML mode.
showLogo true Show an available logo.
logoUrl / logoDataUrl empty Logo URL or data URL.
logoWidth / logoHeight 26 CSS px Maximum logo box.
backgroundColor / textColor #F8F9FA / #5F6368 Built-in layer colors.

Only use trusted HTML, or sanitize user-authored content first. A remote logo must be accessible under browser CORS rules.

Vue footer slot

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

<template>
  <Html2Pdf :footer="{ height: 58 }" :manual-pagination="true">
    <template #pdf-content><article>Document content</article></template>
    <template #pdf-footer="{ pageNumber, totalPages }">
      <div class="report-footer">
        <span>My company</span>
        <span>Page {{ pageNumber }} of {{ totalPages }}</span>
      </div>
    </template>
  </Html2Pdf>
</template>

<style scoped>
.report-footer {
  box-sizing: border-box;
  display: flex;
  justify-content: space-between;
  width: 100%;
  padding: 10px 20px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

The slot activates the footer and owns visible markup. Object text, HTML, logo and template do not appear unless you render them. orientation is also a slot prop. Text mode renders supported slot text as PDF text; Canvas mode captures an image. Chrome may allow OCR selection on an image PDF, so selection alone does not prove native text is present. Scoped and inherited styles are snapshotted within renderer limits.

The built-in {total} and slot totalPages count generated pages before composition, excluding inserted template pages.


Security

Create password-protected PDFs with the security prop.

Security options

Option Type Default Description
enabled boolean compatibility-resolved Explicitly enable/disable the direct security layer.
userPassword string '' Password required to open the PDF.
ownerPassword string '' Owner-level password used by the PDF encryption layer.
passwordParts `Array<string \ number>` []
passwordSeparator string '' Separator used when joining passwordParts.
permissions PdfPermission[] viewer-dependent Requested PDF permission flags.
import type { PdfSecurityOptions } from "vue-pdf-export";

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

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

Permission flags

type PdfPermission = "print" | "modify" | "copy" | "annot-forms";
Enter fullscreen mode Exit fullscreen mode
Permission What it allows
print Allows printing when the PDF viewer enforces document permissions.
copy Allows text/content copying and extraction when the viewer enforces permissions.
modify Allows document modification/editing when supported by the viewer.
annot-forms Allows 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.
  • Caller-supplied header, footer, and slot HTML is sanitized with DOMPurify before insertion into export staging. Supply trusted, intentional document content; sanitization is not a substitute for application security.
  • 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: "-",
};
Enter fullscreen mode Exit fullscreen mode

This creates one final open password:

15081999-4821
Enter fullscreen mode Exit fullscreen mode

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.

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:

19 test files passed
106 / 106 tests passed
Enter fullscreen mode Exit fullscreen mode

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.


Watermarks

The watermark controls below are available in the published package. Choose an explicit layer when placement matters.

An enabled watermark without an explicit layer uses foreground. Set layer: "background" if the mark should appear behind supported selectable-text page content. Opaque content can cover a background watermark.

Design presets

design supplies defaults; every explicit option overrides its preset. For example, classic can still be used with type: 'image' and mode: 'repeat'.

Design Default appearance
classic Existing text/image defaults
diagonal Single angled status text
microtext Small, visibly repeated text
tile Repeated medium text
logo Single subtle image
page-background Faint image covering selected pages
custom Explicit fields only
import type { WatermarkOptions } from "vue-pdf-export";

const watermark: WatermarkOptions = {
  enabled: true,
  design: "classic",
  type: "image",
  mode: "repeat",
  layer: "foreground",
  imageDataUrl: logoPngDataUrl,
  opacity: 0.14,
  rotation: -32,
  width: 20,
  gapX: 5,
  gapY: 5,
  pages: "all",
};
Enter fullscreen mode Exit fullscreen mode

imageDataUrl supports PNG or JPEG data URLs; imageUrl supports a reachable URL with appropriate CORS. Width and gaps use the configured jsPDF units. The source aspect ratio determines height when omitted. x and y position a single mark; repeated marks use a grid and gaps. A transparent PNG usually gives the cleanest foreground logo. Dense repeated images are composed into a reusable transparent page sheet. Full-page PNG backgrounds can increase file size.

For a styled stamp, pass a trusted mounted element, for example watermark: { enabled: true, type: 'html', htmlElement: stampRef.value } after the ref exists. The element is captured as an image once per export. Do not pass an untrusted HTML string. microtext is visible decoration, not a digital signature or tamper protection.

Selectable-text output can draw a real background mark behind its PDF content. Canvas output has opaque raster pages and uses a light foreground fallback to keep a requested background mark visible. An opaque content card can cover a selectable-text background mark. See media and typography.

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,
};
Enter fullscreen mode Exit fullscreen mode
<Html2Pdf :watermark="watermark" />
Enter fullscreen mode Exit fullscreen mode

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,
};
Enter fullscreen mode Exit fullscreen mode

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,
};
Enter fullscreen mode Exit fullscreen mode

A data URL can also be used:

const watermark = {
  enabled: true,
  type: "image",
  imageDataUrl: "data:image/png;base64,...",
};
Enter fullscreen mode Exit fullscreen mode

imageDataUrl takes priority over imageUrl.

Per-page watermark targeting

const watermark = {
  enabled: true,
  type: "text",
  text: "INTERNAL",
  pages: "except-first",
};
Enter fullscreen mode Exit fullscreen mode

Explicit pages:

const watermark = {
  enabled: true,
  text: "INTERNAL",
  pages: [1, 3, 5],
};
Enter fullscreen mode Exit fullscreen mode

Supported targets:

all
first
last
except-first
number[]
Enter fullscreen mode Exit fullscreen mode

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

Watermark options

Option Type Description
enabled boolean Enable/disable watermark.
type `'text' \ 'image' \
{% raw %}design preset name Optional design defaults; explicit options take precedence.
layer `'background' \ 'foreground'`
mode `'single' \ 'repeat'`
text string Text content.
fontSize number Text size.
fontFamily string Font family.
fontWeight `string \ number`
fontStyle `'normal' \ 'bold' \
{% raw %}color string Watermark color.
imageUrl string Image URL.
imageDataUrl string Data URL/base64 image.
width number Image width.
height number Optional image height.
opacity number Clamped to 0..1.
rotation number Rotation in degrees.
x number Horizontal position.
y number Vertical position.
gapX number Horizontal repeat gap.
gapY number Vertical repeat gap.
pages PdfPageTarget Target pages.

Metadata & Compression

Metadata options

Option Type Description
title string PDF document title.
author string PDF author.
subject string PDF subject.
keywords string[] PDF keywords.
creator string PDF creator/application value.
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",
};
Enter fullscreen mode Exit fullscreen mode
<Html2Pdf :metadata="metadata" />
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

compress controls jsPDF stream compression.

It does not lower html2canvas resolution or JPEG image quality.

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


Programmatic API

Component methods

Method Return Purpose
generatePdf({ composition? }?) Promise<Blob> Generate the PDF and optionally override composition.
generatePdfArrayBuffer() Promise<ArrayBuffer> Generate once, then serialize the resulting Blob to binary bytes.
generatePdfBase64(options?) Promise<string> Generate once, then return raw Base64 or an optional PDF data URL.
print() Promise<void> Generate/open the native browser print flow.
closePreview() void Close the active preview flow.
resetPagination() void Remove automatically inserted page breaks.
cancelGeneration() void Request cooperative cancellation of active generation.
interface Html2PdfInstance {
  generatePdf: (overrides?: {
    composition?: PdfCompositionOptions;
  }) => Promise<Blob>;
  generatePdfArrayBuffer: () => Promise<ArrayBuffer>;
  generatePdfBase64: (options?: PdfBase64Options) => Promise<string>;
  print: () => Promise<void>;
  closePreview: () => void;
  resetPagination: () => void;
  cancelGeneration: () => void;
}
Enter fullscreen mode Exit fullscreen mode

Recommended ref:

const pdfRef = ref<InstanceType<typeof Html2Pdf> | null>(null);
Enter fullscreen mode Exit fullscreen mode

Generate Blob

const blob = await pdfRef.value?.generatePdf();
Enter fullscreen mode Exit fullscreen mode

To insert existing PDF pages for this call, use await pdfRef.value?.generatePdf({ composition: { prepend: templatePdf, append: templatePdf } }). The method option wins over the component prop. See PDF composition.

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

Generate ArrayBuffer

const arrayBuffer = await pdfRef.value?.generatePdfArrayBuffer();
Enter fullscreen mode Exit fullscreen mode

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

Generate Base64

Raw Base64:

const base64 = await pdfRef.value?.generatePdfBase64();
Enter fullscreen mode Exit fullscreen mode

PDF data URL:

const dataUrl = await pdfRef.value?.generatePdfBase64({
  dataUrl: true,
});
Enter fullscreen mode Exit fullscreen mode

Base64 is convenient for string-only transports, but it is larger than the 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".

Low-level output serialization

The output helpers are also available directly:

import {
  pdfBlobToArrayBuffer,
  pdfBlobToBase64,
  serializePdfOutput,
} from "vue-pdf-export";

const bytes = await pdfBlobToArrayBuffer(blob);
const base64 = await pdfBlobToBase64(blob);
const dataUrl = await pdfBlobToBase64(blob, { dataUrl: true });

const output = await serializePdfOutput(blob, "arraybuffer");
Enter fullscreen mode Exit fullscreen mode

Supported output formats are blob, arraybuffer, and base64.

Print

await pdfRef.value?.print();
Enter fullscreen mode Exit fullscreen mode

Print works through the browser print flow in both Canvas and selectable-text rendering modes.

Close preview

pdfRef.value?.closePreview();
Enter fullscreen mode Exit fullscreen mode

Reset automatic pagination

pdfRef.value?.resetPagination();
Enter fullscreen mode Exit fullscreen mode

This removes only automatically inserted page breaks.

Cancel active generation

pdfRef.value?.cancelGeneration();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Loader, Progress, Metrics, Events & Slots

Built-in loader

<Html2Pdf :show-loader="true" loader-text="Generating PDF..." />
Enter fullscreen mode Exit fullscreen mode

Disable it:

<Html2Pdf :show-loader="false" />
Enter fullscreen mode Exit fullscreen mode

Numeric progress

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

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

Rich progress stages

<Html2Pdf @progress-stage="onProgressStage" />
Enter fullscreen mode Exit fullscreen mode
import type { PdfProgressState } from "vue-pdf-export";

function onProgressStage(state: PdfProgressState) {
  console.log(state.stage, state.progress);
}
Enter fullscreen mode Exit fullscreen mode

Current stages:

idle
pagination
images
preparing
rendering
watermark
header
footer
metadata
serializing
complete
error
Enter fullscreen mode Exit fullscreen mode

Generation metrics

Metrics are opt-in. When disabled, metrics collection does not add generation work.

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

function onGenerationComplete(payload: PdfGenerationCompletePayload) {
  console.log(payload.metrics);
}
Enter fullscreen mode Exit fullscreen mode

A completed generation can expose total generation time, stage timings, per-page timings, render mode, page count, and generated Blob size.

interface GeneratePdfResult {
  pdf: jsPDF;
  blob: Blob;
  url: string;
  metrics?: PdfGenerationMetrics;
}
Enter fullscreen mode Exit fullscreen mode

Use timing data for profiling and regression checks rather than as a hard real-time performance guarantee.

Custom loader

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

Slot values:

Value Type
progress number
loading boolean
stage PdfProgressStage
progressState PdfProgressState

Events

<Html2Pdf
  @progress="onProgress"
  @progress-stage="onProgressStage"
  @generation-complete="onGenerationComplete"
  @startPagination="onStartPagination"
  @hasPaginated="onPaginated"
  @beforeDownload="onBeforeDownload"
  @hasDownloaded="onDownloaded"
  @closed="onClosed"
  @error="onError"
/>
Enter fullscreen mode Exit fullscreen mode
Event Payload Description
progress number Numeric generation progress.
progressStage / @progress-stage PdfProgressState Named stage and progress.
generationComplete / @generation-complete PdfGenerationCompletePayload Generation finished; includes opt-in metrics when enabled.
startPagination none Pagination started.
hasPaginated none Pagination completed.
beforeDownload BeforeDownloadPayload Emitted before generation.
hasDownloaded Blob Generated Blob is available.
closed none Preview/generation flow closed.
error Error Generation or print error.
paginationBreak / @pagination-break PdfPaginationDiagnostic A planned break; emitted with paginationDebug.
paginationWarning / @pagination-warning PdfPaginationDiagnostic An oversized or other planning warning; debug only.
paginationComplete / @pagination-complete PdfPaginationDiagnostic[] All decisions for this planning pass; debug only.

Slots

Slot Required Description
pdf-content yes HTML converted to PDF.
loader no Custom loader with { progress, loading, stage, progressState }.
pdf-header no Vue markup for each targeted header page; { pageNumber, totalPages, orientation }.
pdf-footer no Vue markup for each generated footer page; same context.

The layer slots activate their layers, own visible content and render differently in Canvas versus text mode. Give the slot root full width and explicit padding. header.height, header.pages and footer.height still apply. See copyable header and footer examples, and pagination diagnostics.


TypeScript

The package ships TypeScript declarations.

The TypeScript declarations include PdfCompositionOptions, PdfTemplateSource, PdfPaginationDiagnostic, and PdfPaginationDiagnosticType. See the current TypeScript documentation for additional details.

Common types:

import type {
  BeforeDownloadPayload,
  FooterMode,
  FooterOptions,
  PdfCompositionOptions,
  PdfTemplateSource,
  PdfPaginationDiagnostic,
  PdfPaginationDiagnosticType,
  GeneratePdfResult,
  HeaderOptions,
  Html2CanvasOptions,
  Html2PdfOptions,
  Html2PdfProps,
  JsPdfOptions,
  PdfAutoFitOptions,
  PdfBase64Options,
  PdfEncryptionOptions,
  PdfFormat,
  PdfGenerationCompletePayload,
  PdfGenerationMetrics,
  PdfImageOptions,
  PdfMargin,
  PdfMetadataOptions,
  PdfOrientation,
  PdfOutputFormat,
  PdfPageBreakOptions,
  PdfPageContext,
  PdfPageTarget,
  PdfPermission,
  PdfProgressStage,
  PdfProgressState,
  PdfRenderMode,
  PdfSecurityOptions,
  PdfSerializedOutput,
  PdfStyle,
  PdfStylingOptions,
  WatermarkLayer,
  WatermarkMode,
  WatermarkOptions,
  WatermarkType,
} from "vue-pdf-export";
Enter fullscreen mode Exit fullscreen mode

Render mode type:

export type PdfRenderMode = "canvas" | "text";
Enter fullscreen mode Exit fullscreen mode

Output types:

export type PdfOutputFormat = "blob" | "arraybuffer" | "base64";

export interface PdfBase64Options {
  dataUrl?: boolean;
}

export type PdfSerializedOutput = Blob | ArrayBuffer | string;
Enter fullscreen mode Exit fullscreen mode

Useful helpers:

import {
  createPdfOptions,
  normalizePdfFilename,
  pdfBlobToArrayBuffer,
  pdfBlobToBase64,
  resolvePdfPages,
  serializePdfOutput,
} from "vue-pdf-export";
Enter fullscreen mode Exit fullscreen mode

Filename Normalization

import { normalizePdfFilename } from "vue-pdf-export";

normalizePdfFilename("invoice:/customer?2026");
// invoice--customer-2026.pdf
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

PDF Styling & data-pdf-ignore

Exclude content from the PDF

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

<div>
  <h1>Invoice</h1>

  <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>
Enter fullscreen mode Exit fullscreen mode
Markup PDF behavior
data-pdf-ignore excluded
data-pdf-ignore="true" excluded
data-pdf-ignore="false" included

Ignored content is removed only from the temporary export copy and is also excluded from export pagination measurements.

Export-only styling

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,
  },
};
Enter fullscreen mode Exit fullscreen mode
<Html2Pdf :styling="styling">
  <template #pdf-content>
    <article>
      <h1 class="invoice-title">Invoice</h1>
      <button class="screen-only">Application action</button>
    </article>
  </template>
</Html2Pdf>
Enter fullscreen mode Exit fullscreen mode

Styling options

Option Type Purpose
enabled boolean Master styling switch.
pageBackground string PDF page surface background.
bodyBackground string Inner body/content surface background.
pageClass string Export-only page class names.
bodyClass string Export-only body class names.
pageStyle `PdfStyle \ PdfStyleResolver`
bodyStyle PdfStyle Static body styles.
cssText string Export-only scoped CSS text.
stylesheetUrls string[] External export stylesheet URLs.
printStylesheetUrls string[] External print/export stylesheet URLs.
theme `'current' \ 'light' \
{% raw %}autoFit `boolean \ PdfAutoFitOptions`

Temporary export styles, themes, stylesheets, and staging DOM are cleaned up after generation.

Customize the built-in preview and loader

In the current documentation version, the built-in preview and loader inherit --vue-pdf-* CSS variables from an ancestor of <Html2Pdf>. They have defaults, so theming is optional. These variables style package UI; they do not style the content inside #pdf-content or the generated PDF. Use your document CSS and the styling option above for the exported pages.

<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);
</script>

<template>
  <div class="report-export">
    <button type="button" @click="pdfRef?.generatePdf()">Preview PDF</button>
    <Html2Pdf
      ref="pdfRef"
      preview-modal
      :enable-download="false"
      manual-pagination
    >
      <template #pdf-content>
        <article>
          <h1>My report</h1>
          <p>Content from my Vue app.</p>
        </article>
      </template>
    </Html2Pdf>
  </div>
</template>

<style>
.report-export {
  --vue-pdf-preview-width: 900px;
  --vue-pdf-preview-surface: #f8fafc;
  --vue-pdf-preview-close-background: #17324a;
  --vue-pdf-preview-focus-color: #0b9668;
  --vue-pdf-loader-surface: #f8fafc;
  --vue-pdf-loader-spinner-accent: #0b9668;
}
</style>
Enter fullscreen mode Exit fullscreen mode
Variable Built-in UI area
--vue-pdf-preview-width, --vue-pdf-preview-height Desktop dialog size.
--vue-pdf-preview-mobile-width, --vue-pdf-preview-mobile-height Preview size at widths up to 640px; defaults fill the viewport.
--vue-pdf-preview-radius, --vue-pdf-preview-backdrop Dialog corners and modal backdrop.
--vue-pdf-preview-surface, --vue-pdf-preview-color Preview surface and foreground.
--vue-pdf-preview-close-background, --vue-pdf-preview-close-color, --vue-pdf-preview-focus-color Close button colors and focus outline.
--vue-pdf-loader-backdrop, --vue-pdf-loader-surface, --vue-pdf-loader-color Loader overlay, panel and text.
--vue-pdf-loader-radius, --vue-pdf-loader-spinner-track, --vue-pdf-loader-spinner-accent Loader corners and spinner.
--vue-pdf-layout-background Optional visible fixed export layout background.

The preview uses a native dialog: Escape or its labeled close button closes it, focus returns to the previous control, and the dialog fills the mobile viewport by default. The default loader exposes status and bounded progress values and stops its spinner when reduced motion is requested. These controls support keyboard use, but you should still test your document markup and the browser's PDF viewer with your target assistive technology. Replace the default loader with the #loader slot if your app needs different UI.


Compose Existing PDF Pages

composition.prepend and composition.append insert whole PDF pages before and after generated pages. They do not overlay a page. A two-page template on both sides adds four pages.

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

const pdfRef = ref<InstanceType<typeof Html2Pdf> | null>(null);
const templatePdf = ref<Blob | null>(null);
const composition = ref<PdfCompositionOptions>({});

function chooseTemplate(event: Event) {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (file) {
    templatePdf.value = file;
    composition.value = { prepend: file, append: file };
  }
}

async function exportPdf() {
  if (!templatePdf.value) return;
  await pdfRef.value?.generatePdf({ composition: composition.value });
}
</script>

<template>
  <input type="file" accept="application/pdf,.pdf" @change="chooseTemplate" />
  <button type="button" :disabled="!templatePdf" @click="exportPdf">
    Export combined PDF
  </button>
  <Html2Pdf
    ref="pdfRef"
    :manual-pagination="true"
    :enable-download="true"
    :composition="composition"
  >
    <template #pdf-content><article>Generated document</article></template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

PdfTemplateSource accepts Blob, ArrayBuffer, Uint8Array, or a URL string pointing to a real PDF. Browser fetch() of remote URLs needs CORS access. Password-protected template PDFs may not load; verify the source before export. The same source used twice is resolved once per generation. The composition engine loads only when needed.

Method generatePdf({ composition }) takes precedence over the composition prop. The object prop takes precedence over the convenience pair pdf-template and template-position="prepend|append|both"; that pair defaults to prepend. Composition can also be passed through the lower-level options. Generated headers, footers, watermarks, and {total} are applied before inserted pages, so their page numbering excludes templates. Cancellation is checked around fetch, parsing, and serialization. The implementation reapplies metadata and encryption to the combined output when configured, but test the resulting permissions and metadata in your target viewers.

For Blob/ArrayBuffer/Base64 output, see the programmatic API. The playground offers composition upload and page-order checks.


Use Cases

Playground

The live playground is the fastest way to compare Canvas and selectable-text rendering and exercise the package without installing it.

Open the interactive PDF playground
Open the live playground

What you can exercise

Upload an existing PDF to try page composition and compare first, last and both insertion. Switch headers and footers between typed objects and Vue slots, inspect smart splitting with table fixtures, show pagination boundaries with the debug toggle, and try different paper sizes. The playground works on desktop, tablet and mobile. Its demo controls and fixtures are not npm exports.

Choose a renderer and paper size

Compare Canvas visual capture with selectable-text output, then choose A/B/C, Letter, Legal or other paper formats and orientation. Use the smart-split and table fixtures to inspect real pagination, including repeated headings.

Try headers, footers and composition

Switch between the typed header object and Vue slot and footer object and Vue slot. Upload a real PDF for composition, then compare first/last/both insertion and component-prop, imperative or legacy configuration. Check the generated page count because optional media and authored breaks change the report length.

Inspect output and document options

Try Blob, ArrayBuffer and Base64 output, PDF preview, download and print. Test auto-fit and export-only styling, passwords and permissions, watermarks, metadata and compression. The debug toggle shows pagination boundaries; its overlay is not embedded in the PDF.

The playground has an optional four-chart ApexCharts sample (bar, line, area, and donut), a custom font picker, image controls, and watermark design presets. Numeric labels and fixed chart callouts can differ between Canvas and selectable text; compare the generated PDFs.

  • Canvas and selectable-text rendering modes
  • selectable/searchable/copyable PDF text
  • preview, download and native browser print
  • Blob, ArrayBuffer, raw Base64 and PDF data-URL output
  • manual and automatic pagination
  • headers, footers and logos
  • watermarks and page targeting
  • passwords, permissions, metadata and compression
  • clickable links
  • progress stages and page-aware progress
  • opt-in total, stage and per-page generation timings
  • cancellation and recovery
  • data-pdf-ignore
  • export-only page/body backgrounds, classes and styles
  • per-page styling callbacks
  • scoped custom PDF CSS
  • external print stylesheets
  • forced current/light/dark themes
  • width auto-fit
  • generated usage code

Stress playground

The interactive /stress.html presets configure a run; selecting a preset does not start PDF generation. Start a run explicitly and review browser memory and output for your device.

The dedicated stress page exposes page-count presets for large-document validation:

  • 5
  • 10
  • 25
  • 50
  • 100
  • 200
  • 500
  • 1000

Open the large-document stress playground
Open the stress playground

Heavy and extreme presets can consume significant browser CPU and memory. The stress UI includes warnings and preserves cancellation/recovery behavior.

The package default remains Canvas mode for backwards compatibility even if the playground chooses text mode for demonstration.


Selectable Text Mode

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

How do I generate a searchable PDF in Vue 3?

Install vue-pdf-export, import Html2Pdf and its stylesheet, set render-mode="text", then call generatePdf() from a component ref after the Vue component mounts. The returned value is a PDF Blob; with enable-download enabled, the component can also download it. This example uses the current documented package API.

npm install vue-pdf-export
Enter fullscreen mode Exit fullscreen mode
<script setup lang="ts">
import { ref } from "vue";
import { Html2Pdf } from "vue-pdf-export";
import "vue-pdf-export/style.css";

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

async function downloadReport() {
  const blob = await pdf.value?.generatePdf();
  if (blob) console.log(`Generated ${blob.size} PDF bytes`);
}
</script>

<template>
  <button type="button" @click="downloadReport">Download searchable PDF</button>
  <Html2Pdf
    ref="pdf"
    render-mode="text"
    filename="searchable-report.pdf"
    :enable-download="true"
  >
    <template #pdf-content>
      <article>
        <h1>Monthly report</h1>
        <p>This body text is selectable and searchable.</p>
      </article>
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

The PDF body text can be selected, copied, and searched in a compatible viewer. Test the result with your fonts and browser: not every CSS effect or glyph has a native-text equivalent. In Nuxt, follow the SSR guide and start generation only in the browser.

Vue PDF export: which renderer should I use?

Use renderMode="canvas" when matching complex browser CSS matters most. It uses the html2pdf.js / html2canvas / jsPDF path, so PDF body text is drawn as an image. Use renderMode="text" when users need to select, search or copy body text in the PDF. Both modes work through the same Vue component and support preview, download and Blob output. Start with the Vue quick start, then compare rendering modes.

The package adds Vue components, typed options, slots and pagination controls around these workflows. Selectable-text mode has its own CSS limits below; it is not a pixel-for-pixel replacement for the Canvas path.

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
  • CFF-outline OTF and WOFF/WOFF2 embedding (registered TTF and TrueType-outline OTF are supported)
  • 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".

The fonts option can register TTF or TrueType-outline OTF files for selectable text. Without a registered file, unrecognized CSS font families map to built-in PDF fonts. Inline SVG shapes are rasterized in this mode. See fonts, charts, SVG and images.

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.

Rendering modes reference

Canvas mode

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

<Html2Pdf render-mode="canvas" />
Enter fullscreen mode Exit fullscreen mode

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 use the whole-document html2pdf.js worker; paginated documents can use incremental page captures.

Selectable text mode

<Html2Pdf render-mode="text" />
Enter fullscreen mode Exit fullscreen mode

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" manual-pagination>

  <template #pdf-content>
    <main>
      <h1>Selectable report</h1>
      <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>
Enter fullscreen mode Exit fullscreen mode

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


Clickable PDF Links

Enable PDF links through htmlToPdfOptions.enableLinks:

const htmlToPdfOptions = {
  enableLinks: true,
};
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Images & CORS

Remote images must be accessible to the browser during rendering.

Recommended:

html2canvas: {
  useCORS: true,
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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

Image optimization

Opt-in autoOptimize provides target DPI, JPEG quality, alpha preservation, size caps, and per-export repeated-image reuse. Existing library image quality remains unchanged when it is disabled. A cross-origin image that cannot be decoded is left unchanged and may still be subject to the renderer’s CORS restrictions.


Production Guide

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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 render developer-provided HTML. Header, footer, and slot HTML is sanitized with DOMPurify before export staging. Supply intentional, trusted document markup; sanitization does not turn arbitrary input into trusted application content.

Test fonts and media on the target browser

Wait for project fonts and register supported font bytes when exact selectable-text glyphs matter. Keep autoOptimize opt-in and choose DPI/JPEG quality appropriate to your output. Test SVG icons, chart snapshots, and font fallback in your target browsers.

Keep selectable-mode link styles simple

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 work completed

The performance/stability work includes:

  • 10 / 25 / 50 / 100-page benchmark and regression fixtures
  • generation-time and PDF-size measurement
  • browser-memory experiments and repeated-generation testing
  • raw Canvas/jsPDF isolation tests to separate 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:

Area Before After Improvement
HTML footer without logo 7.82 s 1.43 s ~81.7% less time
HTML footer with logo 11.81 s 1.29 s ~89.1% less time
Footer-heavy PDF size ~46.7 MB ~1.0 MB ~97.9% smaller
Footer no-logo peak heap 102.6 MB 26.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 are tied to the project's benchmark fixtures and test environment. They 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 whole-document html2pdf.js path is used for Canvas documents without 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,
}
Enter fullscreen mode Exit fullscreen mode

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

Area Coverage
Long Canvas documents 10 / 25 / 50 / 100-page regression fixtures
Heavy cross-browser smoke Chromium plus Firefox/WebKit coverage, with the heaviest 50/100-page cases concentrated in Chromium
Selectable-text long documents long multi-page rendering and cancellation/recovery
Image-heavy selectable mode repeated image cache / raster-call regression
Image-heavy Canvas mode page-bounded canvas regression
Cancellation Canvas and selectable-text cancellation + recovery
Cleanup temporary page/snapshot/fixture cleanup assertions
Features headers, footers, watermarks, links, metadata, compression, security

These tests validate the package's test fixtures and supported paths; they are not a universal guarantee that every arbitrary 100-page web document will fit every browser/device memory budget.

Recommended large-document configuration

For large Canvas reports:

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

Use explicit page markers:

<div class="html2pdf__page-break"></div>
Enter fullscreen mode Exit fullscreen mode

For text-heavy reports where supported CSS is sufficient:

<Html2Pdf render-mode="text" />
Enter fullscreen mode Exit fullscreen mode

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 current automated stress and regression suite verifies large-document behavior through 100-page test fixtures.

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
Enter fullscreen mode Exit fullscreen mode

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.

Link looks like text but is not clickable

Make sure links are enabled:

const htmlToPdfOptions = {
  enableLinks: true,
};
Enter fullscreen mode Exit fullscreen mode

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

Link text looks stretched

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Header or footer image is missing

Check:

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

A large source logo displayed at a small size is capped to a proportional raster size during selectable-text header/footer preparation. The source file and caller options remain unchanged.

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

In selectable-text mode, a requested background watermark is drawn behind PDF content, but opaque cards can cover it. Canvas pages are opaque images, so a requested background mark uses a light foreground fallback to remain visible.

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.

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.

A header or footer appears empty

The component accepts direct props as well as header and footer objects. Explicit object fields take priority, while omitted fields may fall back to matching direct props. Check the enabled flag, slot content, object settings, and reserved height. For a new integration, prefer the grouped object API or Vue slots.

Table heading repeats twice, or a row crosses a page cut

Use one semantic <thead> and normal <tbody> rows. Avoid manually duplicated headings. Try smart pagination and its diagnostic overlay. Large rowspans, oversized rows and unusual CSS need manual review in both renderers.

Canvas PDF appears to have selectable text

Chrome can OCR page images. Foreground text watermarks or some numbering paths may also add native text. Inspect extracted PDF text or text-showing operators rather than relying only on mouse selection. Use render-mode="text" for native selectable body text.


Testing & 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.

Testing

The project uses unit, browser, and integration tests for smart splitting, table continuation, slots, PDF composition, page geometry, cancellation, and renderer behavior. Some documented verification runs were followed by targeted fixes; they are not a substitute for rerunning the current suite before release. Inspect representative PDFs, especially continued tables, nested frames, slots, and format/orientation combinations.

A documentation build does not run the package browser suites. For testing strategy, see the testing and browser support guide.

Media and typography verification

Documented media verification includes a 380-test unit run, TypeScript checks, library and playground builds, focused Chromium/WebKit browser exports, and selected rendered-page visual checks. A format/orientation matrix covered 82 combinations. Follow-up pagination and footer changes have narrower checks, including an inspected nine-page selectable-text report. Local Firefox verification remained open because the headless graphics compositor failed at launch. These are named test records rather than a fresh full-suite claim. See media and typography for the tested boundaries.

The release verification covers major public workflows, including:

  • Canvas and selectable-text PDF generation
  • Blob, ArrayBuffer and Base64 output APIs
  • preview Blob creation
  • download
  • native browser print flow
  • rendering mode routing
  • selectable-text generation
  • PDF link annotations
  • pagination and page-break mapping
  • data-pdf-ignore
  • export-only page/body styling
  • themes, custom CSS, external stylesheets and auto-fit
  • metadata
  • compression
  • security
  • protected/unprotected PDFs
  • header page targeting
  • selective header spacing
  • optional header/footer logos
  • custom logo sizing
  • header/footer HTML rendering
  • selectable header/footer text
  • footer page numbering
  • per-page watermark targeting
  • progress stages
  • page-aware currentPage / totalPages
  • opt-in generation metrics
  • cooperative cancellation and recovery
  • portrait and landscape layouts
  • large-document regression/stress scenarios
  • selectable repeated-image caching
  • page-bounded Canvas image-heavy rendering
  • temporary rendering DOM/snapshot cleanup
  • SSR-safe package import
  • Nuxt 3 and Nuxt 4 SSR/hydration/client navigation

Package-manager consumer matrix

Packed-package consumer fixtures run against:

  • npm
  • pnpm
  • Yarn
  • Yarn Plug'n'Play
  • Bun

These fixtures verify install behavior, TypeScript resolution, production builds, CSS imports, and runtime PDF generation.

Because PDF rendering is CPU-heavy, a small Playwright worker count is recommended:

npx playwright test --workers=2
Enter fullscreen mode Exit fullscreen mode

Use a single worker for heavy stress/regression suites:

npx playwright test --workers=1
Enter fullscreen mode Exit fullscreen mode

Run the package's release checks before publishing changes to the PDF library itself. Application consumers should also test representative real documents on the browsers and devices that matter to their users.


Performance Engineering

The large-document milestone was not only a benchmark exercise. It changed the runtime architecture and added repeatable correctness, cleanup and memory diagnostics.

The measurements below are historical fixtures. The latest full browser and stress reruns were not part of this docs update; measure your own document on target devices.

Rendering architecture

The package dynamically loads PDF engines and the optional composition engine, resolves a shared template once, reuses unchanged header/footer and styled slot captures, reduces repeated image waiting, and prunes unrelated app DOM while capturing a layer. Oversized splittable Canvas sections are preflight paginated on private DOM, then fitting fragments are reused. Cooperative yields avoid waiting for hidden-tab animation frames. Generation reuses one final Blob for preview and download, with cleanup, cancellation and opt-in timing metrics.

A local nine-page Canvas/slot export with watermark off was inspected: 27 image objects and zero PDF text-showing commands; a separate text-mode PDF contained 423 text commands. One local Canvas slot session changed from 26.89 seconds to 12.55 seconds after the refinements, without a controlled benchmark. A separate nine-page text run was about 4.35 seconds; no text-mode speedup was established. These observations do not predict your document's runtime or fidelity. Record browser/device, page count, renderer, assets, quality and cold/warm state when comparing exports.

Executive outcome

  • paginated Canvas documents can render page by page instead of one giant document-height canvas
  • 50-page and 100-page Canvas exports are verified as real, non-empty PDFs on the incremental path
  • static HTML footer content is rasterized once and reused across pages
  • selectable-text mode caches repeated image sources within one generation
  • long work can be cancelled and then safely regenerated
  • progress can expose stage, progress, currentPage and totalPages
  • temporary DOM, Canvas backing stores and encoded image references are released earlier
  • browser yielding was added between expensive chunks
  • encrypted raster memory retention was isolated and reproduced directly in jsPDF

Phase map

Phase Focus Result
Phase 1 Benchmark/stress foundation Actual page count, Blob size, Canvas geometry, timing, memory and progress telemetry.
Phase 2.1 Static HTML footer bottleneck Render once + reuse; major time, output-size and heap reductions in the fixture.
Phase 2.2 Clean memory isolation Fresh-context methodology separated temporary peaks from retained heap.
Phase 2.3A Security isolation Basic user-password encryption identified as the trigger for high raster retention.
Phase 2.3B Large-PDF correctness Page count alone was proven insufficient; Blob size and Canvas telemetry were added.
Phase 2.4 Scaling and cliff diagnostics Raster payload + jsPDF encryption pattern isolated; legacy long-canvas failure instrumented.
Phase 3.1 Incremental Canvas Page-bounded captures replaced the giant capture for paginated Canvas documents.
Phase 3.2 Security re-test Giant-canvas issue addressed; encrypted-raster retention remained dependency-level.
Phase 3.2.1 Raw jsPDF reproduction Strongest retention pattern reproduced without Vue component lifecycle.
Phase 3.3 Cancellation + progress Cooperative cancellation, recovery and current-page progress added.
Phase 3.4 Repeated assets + cleanup Per-generation image cache and shorter DOM/Canvas lifetimes.
Phase 3.5 Routing regression hardening Correct incremental Canvas branch restored and protected by 50/100-page E2E tests.

Headline measured results

Metric Before After Improvement
HTML footer, no logo 7.82 s 1.43 s ~81.7% less time / 5.47Ă— faster
HTML footer + logo 11.81 s 1.29 s ~89.1% less time / 9.16Ă— faster
Footer-heavy PDF ~46.7 MB ~1.0 MB ~97.9% smaller
No-logo peak heap 102.6 MB 26.5 MB ~74.2% lower
Giant Canvas capture geometry ~99.96M px ~1.74M px/page ~98.26% lower peak capture area

These percentages describe specific benchmark fixtures or capture geometry; they are not universal whole-package speed or memory guarantees.

Current incremental Canvas correctness

Requested pages Actual pages Time PDF size Largest encoded Canvas
10 10 1.53 s 1.5 MB 1191 Ă— 1461
25 25 4.33 s 3.9 MB 1191 Ă— 1461
40 40 7.54 s 6.2 MB 1191 Ă— 1461
50 50 9.68 s 7.8 MB 1191 Ă— 1461
60 60 12.56 s 9.3 MB 1191 Ă— 1461
75 75 16.87 s 11.7 MB 1191 Ă— 1461
100 100 25.25 s 15.8 MB 1191 Ă— 1461

The maximum encoded Canvas remained page-sized as the requested page count increased.

Encrypted raster PDFs

The strongest high-retention behavior was reproduced with raw jsPDF, so the project documents it as memory retention rather than mislabeling it as a permanent Vue memory leak.

Upstream issue: jsPDF #4017 - encrypted raster PDF memory retention

For large text-heavy secured documents whose CSS is supported by the vector renderer, selectable-text mode can be much lighter than raster Canvas mode. This is workload-dependent, not a universal renderer benchmark.

Verification mindset

Large exports are checked with more than “the Promise resolved”: actual page count, Blob size, Canvas dimensions, cancellation/recovery and cleanup signals are part of the regression strategy.

Read the complete phase-by-phase engineering report.


Fonts, Charts, SVG and Image Optimization

The following media controls are part of the published vue-pdf-export API. Use the current official reference when integrating them.

vue-pdf-export generates PDFs in the browser from Vue 3 content. It prepares media in a private export clone: it waits for fonts, snapshots chart canvases, applies the SVG policy and optionally optimizes images. The live Vue page stays intact, and temporary DOM, font registrations and caches are cleaned after completion, failure or cancellation.

Configure media for a Vue PDF

<script setup lang="ts">
import { Html2Pdf } from "vue-pdf-export";
import type {
  PdfFontSource,
  PdfImageOptimizationOptions,
} from "vue-pdf-export";

const fonts: PdfFontSource[] = [
  {
    family: "Report Sans",
    source: "/fonts/report-sans.ttf",
    format: "ttf",
    weight: 400,
  },
];
const autoOptimize: PdfImageOptimizationOptions = {
  targetDpi: 144,
  jpegQuality: 0.85,
  preserveAlpha: true,
  cacheRepeated: true,
};
</script>

<template>
  <Html2Pdf
    render-mode="text"
    :fonts="fonts"
    :auto-optimize="autoOptimize"
    svg-mode="auto"
  >
    <template #pdf-content><article>Report content</article></template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

fonts, autoOptimize and svgMode can also be passed through htmlToPdfOptions. Explicit component props win. autoOptimize is off unless you enable it, so existing image quality stays as configured.

Custom fonts and selectable PDF text

Export waits for document.fonts.ready before layout. With no custom fonts list, Canvas uses the page's computed CSS font. Selectable-text mode still measures the CSS layout but maps unregistered font families to built-in PDF faces. Register a matching file when the exact project font must appear as selectable text.

Each PdfFontSource takes family, optional style (normal or italic), optional weight, source (URL, ArrayBuffer or Uint8Array), and optional format (ttf or otf). The CSS font-family must match family. Register the bold and italic files separately if the document uses them. The supported OTF path requires TrueType outlines; CFF-outline OTF, WOFF and WOFF2 cannot be embedded by this path. Remote URLs need working CORS, CSP and permission to use the font. Test the actual glyphs and language in a PDF viewer because shaping remains renderer-dependent.

Canvas charts and inline SVG

Canvas charts are captured as export-only PNG snapshots. Static numeric labels and fixed callouts can be present in the PDF; live hover tooltips cannot remain interactive in a static document. The four-chart ApexCharts sample is playground-only and does not add ApexCharts to the library's runtime requirements.

svgMode Canvas PDF Selectable-text PDF
auto (default) Keep inline SVG in staging; the final page is rasterized Rasterize SVG because the text renderer does not draw SVG shapes
preserve Keep inline SVG in staging Rasterize unsupported SVG shapes
rasterize Snapshot SVG into an image first Rasterize SVG

External SVG resources, filters, animation and foreignObject can differ from the live browser. Test representative charts, logos and icons in both rendering modes.

Image quality and per-export reuse

With autoOptimize, oversized raster images are downscaled from their displayed size and targetDpi (default 144). maxWidth and maxHeight cap output dimensions. jpegQuality defaults to 0.85 and is limited to 0.1–1. preserveAlpha defaults to true, choosing PNG for suitable non-JPEG sources; setting it to false flattens transparency onto white for JPEG. cacheRepeated defaults to true and reuses matching prepared sources, dimensions, format and quality during one export. Selectable-text rendering also has its own per-render image cache. Canvas pages remain raster page images, so object-level reuse is different.

Repeated image watermarks reuse the prepared source within one export. Sparse layouts reuse a PDF image alias; dense tiled layouts reuse one transparent sheet per page size. Static Canvas footer HTML and unchanged slot captures also reuse preparation. Set collectMetrics to inspect metrics.media operation counts, including chart snapshots, SVG rasterizations, optimized images, cache hits, watermark reuse and footer captures. These counters are not a speed or byte-saving guarantee. See watermarks and performance engineering.

CORS, failure and cleanup

Use same-origin media, a server that permits the page origin through CORS, or suitable data URLs. If image optimization cannot decode a cross-origin image, it leaves the staging image in place and warns; the renderer may still fail under its own CORS policy. A CORS-tainted watermark raises an error. Remote fonts can also fail during fetch. Private staging and temporary font registrations are cleaned on success, error and cancellation. See images and CORS for recovery steps.

Media, pagination and cleanup improvements

  • Selectable-text table continuation now places an automatic split and repeated heading before the first row that crowds a measured page boundary. The inspected playground fixture no longer has a row-only page at that boundary. Oversized indivisible cells remain a limit.
  • A large source logo used at a small header/footer size is rasterized proportionally for selectable-text output. A local nine-page fixture shrank from about 973 KB to about 300 KB; other documents can differ.
  • Canvas page encoding uses toBlob and binary bytes where available, with a data-URL fallback. The playground’s JPEG quality was set to 0.88 in the measured demo configuration; that setting is not a library default.
  • Export HTML paths for headers, footers and slots are sanitized before staging insertion. A cancelled preflight and late-result guard prevent a cancelled generation from opening a completed preview.

See pagination, security and project flow for their boundaries.

Verification scope

The library's media verification records unit, TypeScript and build checks, Chromium and WebKit exports, and selected visual PDF inspections. Local Firefox verification failed at the headless graphics launch and remains open. Later pagination and footer fixes have narrower checks. These results cover named fixtures, not arbitrary HTML/CSS or a universal speed target. The testing guide explains how to verify your own content.

The generation-flow guide shows where preparation, rendering and cleanup happen in the package.


PDF Generation Flow

vue-pdf-export exposes an Html2Pdf component and a useHtml2Pdf composable. The component coordinates props, slots, pagination, preview and download. The composable manages generation state, progress, cancellation and output URLs. The core prepares content and chooses a renderer; renderer modules own Canvas or selectable-text output.

From Vue content to PDF Blob

  1. The component resolves options and, when a positive pagination height is supplied, plans DOM page breaks. Direct composable calls provide their own prepared element and page breaks.
  2. Core creates a private export staging clone, applies export-only styles and ignore markers, waits for fonts and images, and prepares chart, SVG and image assets. The live Vue page is left alone.
  3. Selectable-text mode maps supported DOM text, shapes, images and links into PDF operations. Canvas mode captures page images, one page at a time when markers are available, or uses its classic html2pdf path otherwise.
  4. Page styling, watermarks, headers, footers, metadata, optional page composition and security are applied to the PDF.
  5. One serialized Blob feeds preview, download, print or output conversion. Cleanup releases staging content, temporary fonts and media caches; the UI later revokes its owned Blob URLs.

The public library defaults to Canvas. The playground can use its own demo defaults. Nuxt imports are SSR safe, but PDF generation needs a browser DOM. See rendering modes, pagination, media preparation and programmatic output.

For maintainers, the project repository includes source code and engineering context. Check the installed package APIs against the current official documentation.


Find answers in the official vue-pdf-export documentation

For integration questions, search the current official documentation and its API reference. Check the package API and browser limitations before copying an example.

What can this answer?

Ask about selectable-text PDFs, Canvas rendering, Nuxt integration, page breaks, long tables, headers, footers, watermarks, fonts, image quality, and programmatic output. Cross-check answers against the current API reference.

Documentation examples cannot verify arbitrary application CSS or guarantee that a PDF will match every browser layout. Check your own exported PDF. For hands-on examples, see the playground, quick start, rendering modes, and troubleshooting.


Vue to PDF in Vue 3 and Nuxt

To turn a Vue component into a PDF in the browser, install vue-pdf-export, put the document inside its #pdf-content slot, and call generatePdf() from a component ref. Choose Canvas rendering when visual CSS fidelity matters most, or selectable-text rendering when people must search and copy body text.

Generate a PDF from a Vue component

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

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

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

<template>
  <button type="button" @click="downloadReport">Download report</button>
  <Html2Pdf ref="pdf" filename="report.pdf" render-mode="canvas">
    <template #pdf-content>
      <article>
        <h1>Report</h1>
        <p>Rendered from a Vue component.</p>
      </article>
    </template>
  </Html2Pdf>
</template>
Enter fullscreen mode Exit fullscreen mode

The package's default is Canvas. The explicit render-mode above makes the choice visible. For a JavaScript Options API example and download settings, use the quick start. For Nuxt 3 and Nuxt 4, import the component safely during SSR and generate only after the browser mounts; see the Nuxt guide.

Which Vue PDF approach fits the document?

Requirement Start with Tradeoff
Match browser CSS, charts and visual reports Canvas mode The page body becomes an image; readers cannot select its text.
Search, select or copy report text Selectable-text mode Some CSS, glyph and SVG behavior differs from the browser; test the actual document.
Print an entire server-rendered route without a browser user action A server browser such as Playwright page.pdf() in an appropriate Nuxt server route You operate a browser service and handle its server resources and access controls.
Display an existing PDF rather than create one A PDF viewer Viewing and generating are separate tasks.

The html2pdf.js project documents that its Canvas path renders content as an image, so text is not searchable or selectable. vue-pdf-export exposes that style of capture as its Canvas mode and also provides a selectable-text renderer. These modes share the component API, pagination controls, preview, download and print flow.

Can Vue-generated PDFs be searched?

Yes, when you use render-mode="text" for the document body. PDF viewers can then select and search the emitted text. Canvas mode keeps the body as page images. Custom TTF or TrueType-outline OTF files can be registered for selectable text; CFF-outline OTF and WOFF/WOFF2 are not supported for embedding. See fonts, charts, SVG and images for the renderer-specific limits.

Can I combine PDFs in Vue?

You can prepend or append existing PDF pages to a generated document using the published composition options. This combines whole pages; it does not provide arbitrary editing of their internal content.

What should I test before shipping?

Export a representative document in each target browser. Check page breaks, font loading, remote-image CORS, output size, links and text selection as applicable. For long reports, use page-aware progress, cancellation and generation metrics and test a realistic page count. The production guide covers release checks.


Links and feedback

If you are using Vue 3 or Nuxt and have run into PDF pagination, selectable-text, CORS, headers/footers, or browser-memory issues, I would love to hear what you are building and which edge cases are hardest.

If this project has been useful, you can support its development.

Top comments (0)