Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →To export an HTML element as an image in a jsPDF file, render it to a canvas with html2canvas, then pass that canvas or its data URL to jsPDF.addImage(). This browser-side approach gives you control over image format, dimensions, and placement. The main pitfalls are cross-origin images, oversized canvases, and trying to fit long content on one PDF page.
What the HTML-to-jsPDF export actually does
HTML is not itself a PDF image. html2canvas reconstructs a DOM element in the browser and resolves to a canvas. jsPDF then places the rendered pixels into a PDF page. The libraries run client-side; no server rendering is required for this method.
The result is rasterized: the captured content behaves like an image, not like native PDF text. That makes it suitable for screenshots, UI previews, charts, and visual reports, but text in the capture will not be selectable or searchable. If selectable text matters, create that text with jsPDF’s text APIs instead of rasterizing it.
Install the packages and prepare the page
In a project using npm, install jsPDF and html2canvas:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
npm install jspdf html2canvas
Give the part of the page you want to export a stable selector. Keep the element visible and fully laid out when you capture it; hidden or not-yet-rendered content may not appear as expected.
<section id="capture">
<h1>Monthly report</h1>
<img src="/images/chart.png" alt="Monthly totals">
</section>
<button id="export" type="button">Download PDF</button>
Export an HTML element as a jsPDF image
This example exports a single captured element to an A4 portrait PDF. It uses PNG for crisp UI edges and preserves the element’s aspect ratio rather than stretching it to arbitrary dimensions.
import { jsPDF } from 'jspdf';
import html2canvas from 'html2canvas';
async function exportHtmlImage() {
const element = document.querySelector('#capture');
if (!element) throw new Error('Missing #capture element');
const canvas = await html2canvas(element, {
useCORS: true,
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight,
scale: window.devicePixelRatio
});
const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = (canvas.height * pageWidth) / canvas.width;
const image = canvas.toDataURL('image/png');
pdf.addImage(image, 'PNG', 0, 0, pageWidth, pageHeight);
pdf.save('html-export.pdf');
}
document.querySelector('#export')?.addEventListener('click', () => {
exportHtmlImage().catch(error => console.error('PDF export failed:', error));
});
The call to html2canvas is asynchronous: wait for its Promise before using the canvas. windowWidth and windowHeight use the element’s scroll dimensions, which helps capture content beyond its visible box. The scale setting controls pixel density; higher values can improve detail but use more memory.
jsPDF’s addImage accepts a canvas, an image element, a data URL, a typed array, or RGBA data. It needs a format and placement coordinates and dimensions, expressed in the PDF’s chosen units. The example converts the canvas to a PNG data URL. PNG is useful for crisp screenshots and transparency; JPEG is a reasonable choice when lossy compression is acceptable and reducing file size matters. jsPDF also documents WebP format handling.
Recommended Free Tools
Rank #2
Wait for images and dynamic content before capture
If an image has not loaded when capture starts, the canvas can omit it. For ordinary page images, wait for load or decode before calling html2canvas. For images inserted dynamically, await their decode() promises when available.
async function waitForImages(element) {
const images = [...element.querySelectorAll('img')];
await Promise.all(images.map(async image => {
if (image.complete && image.naturalWidth > 0) return;
if (typeof image.decode === 'function') {
try { await image.decode(); return; } catch {}
}
await new Promise((resolve, reject) => {
image.addEventListener('load', resolve, { once: true });
image.addEventListener('error', reject, { once: true });
});
}));
}
await waitForImages(document.querySelector('#capture'));
await exportHtmlImage();
For applications that render charts, web fonts, or other asynchronous components, wait for those components to finish their own rendering too. Image readiness alone does not guarantee that every other part of the DOM is complete.
Handle cross-origin images correctly
A remote image can be visible in the page and still be blocked from a canvas export. Browsers enforce canvas origin restrictions: the image server must permit access by returning an Access-Control-Allow-Origin response header. Setting useCORS: true asks html2canvas to load images using CORS, but it cannot override a server that does not grant permission. As the html2canvas FAQ puts it, “html2canvas cannot circumvent content policy restrictions.”
- Check the image request in the browser’s network tools and confirm the response has an appropriate
Access-Control-Allow-Originheader. - If you control the image host, configure it to permit the origin serving the page.
- If you do not control that server, fetch the image through a same-origin backend or proxy that you operate, subject to the source site’s terms and your security requirements.
- A same-origin data URL can also work when your application is permitted to obtain and convert the asset.
Do not treat useCORS: true as a way around a site’s access controls. Without permitted cross-origin access, the canvas may be tainted or the image may be missing.
Rank #3
Fit content to a PDF page without distortion
PDF page dimensions and canvas pixel dimensions use different units. To preserve the canvas’s proportions, choose the PDF width and derive the height from the canvas aspect ratio, as in the example. Forcing unrelated width and height values stretches the image.
The one-page example places the image at the top of the page at full page width. If its calculated height exceeds the page height, the image extends beyond that page; it does not automatically create additional pages. There are two sensible approaches:
- Scale the entire image to fit one page if legibility remains acceptable.
- Slice the canvas into page-sized sections or render the source content in page-sized sections, adding a PDF page for each section.
For long reports, page-sized sections are usually easier to read than shrinking an entire long page into a single sheet. Keep each slice’s dimensions proportional when adding it, and ensure that the next page begins at a deliberate content boundary where possible.
Export long content across multiple pages
A canvas can become so large that a browser produces a blank or partially rendered result. Browser and platform canvas limits vary, so there is no single safe maximum that applies everywhere. Matching html2canvas’s window dimensions to the element’s scroll size helps with layout, but it does not remove those limits.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
For predictable output, divide long content into sections sized for the PDF page and render each section separately. A basic page loop can look like this when each section is already sized to fit a page:
async function exportSections(sectionElements) {
const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
for (let index = 0; index < sectionElements.length; index++) {
const section = sectionElements[index];
const canvas = await html2canvas(section, {
useCORS: true,
windowWidth: section.scrollWidth,
windowHeight: section.scrollHeight,
scale: window.devicePixelRatio
});
if (index > 0) pdf.addPage();
const imageHeight = (canvas.height * pageWidth) / canvas.width;
const fitScale = Math.min(1, pageHeight / imageHeight);
pdf.addImage(
canvas.toDataURL('image/png'), 'PNG', 0, 0,
pageWidth * fitScale, imageHeight * fitScale
);
}
pdf.save('html-sections.pdf');
}
This example fits each section within one page and preserves its proportions. If a section is taller than a page, it will be scaled down; split that section further if the result would be difficult to read. Capture each section only after its content and images are ready.
Choose between addImage, jsPDF.html, and html2pdf.js
| Approach | What it does | Best fit | Trade-off |
|---|---|---|---|
html2canvas + addImage |
Renders a chosen DOM element to a canvas, then places the image in jsPDF. | Direct control of image format, dimensions, placement, and page slicing. | You handle image readiness, sizing, CORS constraints, and page layout. |
jsPDF.html |
Provides a convenience HTML-to-PDF route that depends on html2canvas; string HTML also has an optional dompurify dependency. | A quick all-in-one export when you do not need as much direct image-placement control. | It still relies on browser rendering and does not eliminate canvas or cross-origin constraints. |
html2pdf.js |
Automates HTML-to-PDF conversion and offers CSS, legacy, and avoid-all page-break modes. |
When page-break automation is more important than explicitly controlling each image placement. | Its documented output rasterizes content into an image, so text is not selectable or searchable and the file can be larger. |
Choose based on the document’s needs: explicit canvas plus addImage is useful when you want to decide exactly what image goes where; the convenience routes reduce some setup but do not turn rasterized content into native PDF text.
Common problems and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
| Remote image is missing or the export fails | The image host does not grant cross-origin access, or the canvas is tainted. | Inspect the response headers. Use useCORS: true only when the host permits it; otherwise serve the asset through an authorized same-origin route. |
| Export is blank or cropped | The capture is too large, or the viewport dimensions do not reflect the element’s full content. | Set windowWidth and windowHeight from scroll dimensions. Reduce the capture size or render separate page-sized sections. |
| Image looks blurry | The canvas has too few pixels for its display size in the PDF. | Increase scale before capture, then preserve the aspect ratio. Higher scale increases memory use and may worsen oversized-canvas failures. |
| Image is stretched | The PDF width and height were set independently without respecting the canvas ratio. | Calculate height as canvas.height * pageWidth / canvas.width, then scale proportionally if fitting the page. |
| PDF is huge or its text cannot be searched | The HTML was rasterized into an image. | Use a more compact image format where acceptable, reduce unnecessary pixel dimensions, or generate important text with jsPDF’s text APIs. |
| Some images appear inconsistently | Capture began before images or dynamic content finished loading. | Wait for image loads or decode(), and wait for application-specific rendering to complete. |
Performance, reliability, and file-size trade-offs
Canvas memory use grows with pixel dimensions; increasing both width and height multiplies the number of pixels. A higher scale can improve sharpness, but it also increases memory pressure. Large, full-page captures are more likely to hit browser-specific limits, so test representative content in the browsers and devices your users support, and prefer smaller sections for long documents.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
PNG preserves sharp edges and transparency but can produce larger files for complex captures. JPEG uses lossy compression and can reduce size when transparency and pixel-perfect edges are not required. Neither format makes rasterized text searchable. If both visual fidelity and searchable text are requirements, consider building the PDF with native text and image elements rather than exporting the whole DOM as one image.
There is no server-side rendering step in this recipe: the browser’s current page state is what gets captured. That is useful for client-side apps and user-triggered exports, but it means the page must be loaded, the relevant assets accessible, and the browser able to render the chosen content.
Or skip the browser setup
If your goal is a screenshot or PDF of a live website rather than exporting the DOM already open in your app, ScreenshotNeo offers a one-request screenshot API and an MCP server for AI agents. It returns a PNG, JPEG, WebP, or PDF; its features include full-page capture, element capture by CSS selector, viewport and device options, and PDF controls. For HTML-to-PDF work that specifically needs native selectable text, a screenshot API is not a substitute for constructing the PDF with text objects.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Cookie and consent banners are accepted like a visitor and removed, along with known newsletter popups and chat widgets, before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots a month without a card; paid plans start at $5 for 3,000 shots.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Sign up free for 1,000 screenshots a month, with no card required.
Frequently Asked Questions
Can jsPDF add a canvas directly without converting it to a data URL?
Yes. jsPDF’s addImage accepts a canvas as well as a data URL, image element, typed array, or RGBA data.
Does jsPDF.html require html2canvas?
Yes. The jsPDF html method depends on html2canvas; when given a string HTML document, dompurify is an optional dependency.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




