Production Guide
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;
}
Keep export CSS separate from responsive application CSS
The web UI and the PDF document have different layout goals.
Choose the renderer intentionally
Use canvas when exact visual fidelity is the priority.
Use text when selectable/searchable/copyable PDF text is the priority and the document uses CSS supported by the vector renderer.
Avoid large fixed heights
Prefer min-height over large fixed height values where possible.
Protect important blocks
Use .pdf-keep-together, .pdf-no-break, or carefully chosen pagebreak.avoid selectors.
Preserve image aspect ratios
img {
max-width: 100%;
height: auto;
object-fit: contain;
}
Keep header/footer content inside configured heights
Very large text or HTML content can overlap PDF content if it exceeds headerHeight or footerHeight.
Use trusted HTML only
headerHtml and footerHtml intentionally render developer-provided HTML.
Never pass unsanitized user-controlled content.
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 legacy whole-document html2pdf.js path is retained for Canvas documents that do not contain page-break markers.
Selectable-text image optimization
Selectable-text rendering uses a per-generation image cache for repeated real <img> sources when the source, target raster dimensions, format, and JPEG quality match.
Why the cache is intentionally per-generation:
- avoids carrying stale DOM/image state across exports
- keeps cache lifetime bounded
- prevents a global cache from becoming a long-lived memory owner
Canvas elements are not cached because their pixels may change even when their dimensions do not.
Cleanup and browser responsiveness
Long exports explicitly release temporary resources where practical:
- off-screen incremental page DOM is removed after
html2canvasno 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:
- an active long export can be cancelled,
- temporary rendering DOM is cleaned up,
- no successful Blob is exposed for the cancelled run, and
- a new PDF can be generated successfully afterwards.
Page progress
Page-aware renderers can report:
{
stage: 'rendering',
progress: number,
currentPage?: number,
totalPages?: number,
}
This is useful for long reports where a percentage alone does not explain what the renderer is doing.
Encrypted Canvas memory behavior
Large encrypted Canvas PDFs can retain substantially more memory than equivalent unencrypted Canvas PDFs in Chromium.
Isolation testing reproduced the severe encrypted-raster retention pattern with raw jsPDF usage, outside the Vue component lifecycle. For that reason this is tracked as an upstream/dependency concern rather than described as a confirmed Vue memory leak.
Practical guidance:
- avoid unnecessarily high Canvas scale values
- resize oversized images before PDF generation
- generate one very large document at a time
- prefer selectable-text mode when it meets the document's visual requirements
- test encrypted long documents on representative target devices
- consider splitting exceptionally large documents when browser memory is constrained
Upstream jsPDF tracking: large encrypted Canvas PDFs can show substantially higher memory retention than equivalent unencrypted Canvas PDFs in Chromium. The same encrypted-raster pattern was reproduced in a raw jsPDF-only test outside the Vue component lifecycle, so this is tracked as an upstream/dependency concern rather than described as a confirmed Vue memory leak. See jsPDF issue #4017.
Upstream tracking: jsPDF #4017 - encrypted raster PDF memory retention.
This link is included so developers using large encrypted Canvas PDFs can follow the dependency-level investigation directly.
Stress/regression coverage
| 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" />
Use explicit page markers:
<div class="html2pdf__page-break"></div>
For text-heavy reports where supported CSS is sufficient:
<Html2Pdf render-mode="text" />
Selectable-text mode avoids rasterizing the entire body and can be a better fit for long text-first documents.
About documents larger than 100 pages
The 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
