Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use one PDF instance, capture each element with await html2canvas(), add each resulting canvas to that document in order, and insert pages when needed. This keeps cards, charts, or sections independent while giving you precise control over page breaks and margins. If the elements already form one document layout, capturing their common container can be simpler.

Choose the capture strategy first

Approach Best when Main trade-off
Capture a shared container The browser layout should stay together exactly as one composition. One very tall canvas can exceed browser or platform limits and become blank or partially rendered.
Capture each element separately Sections need controlled order, independent scaling, or separate PDF pages. You must calculate placement, margins, and page breaks yourself.
Use html2pdf.js You want an integrated client-side flow based on html2canvas and jsPDF, with documented page-break controls. Its layout behavior still depends on html2canvas rendering and the PDF options you choose.

html2canvas reconstructs a page from DOM information; it is not a literal browser screenshot and only supports CSS properties it understands. The project describes it as taking “screenshots” of webpages or parts of them directly in the user’s browser. The capture is asynchronous, so every canvas must resolve before you finalize the PDF.

Prerequisites and document design

  • Run this in a modern evergreen browser (Chrome/Chromium, Firefox, or Safari). html2canvas depends on browser APIs and is not a Node.js server renderer.
  • Install html2canvas and jspdf in your web application. The example below uses ES-module imports, so your bundler resolves both packages.
  • Give each target a stable selector such as data-pdf-section. Avoid selecting transient overlays, sticky navigation, or elements that are still animating.
  • Decide whether every section starts on a new PDF page. A single canvas image can also be scaled to fit a page, but shrinking very tall content may make text hard to read.

Complete JavaScript example: one element per page

This example captures elements in DOM order and places each image on its own A4 page. It uses millimetres for PDF coordinates and preserves each canvas’s aspect ratio.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';

const targets = [...document.querySelectorAll('[data-pdf-section]')];
if (!targets.length) throw new Error('No PDF sections found');

const pdf = new jsPDF({
  orientation: 'portrait',
  unit: 'mm',
  format: 'a4'
});

const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
const margin = 10;
const maxWidth = pageWidth - margin * 2;
const maxHeight = pageHeight - margin * 2;

for (let index = 0; index < targets.length; index += 1) {
  const canvas = await html2canvas(targets[index], {
    backgroundColor: '#ffffff',
    scale: Math.min(window.devicePixelRatio || 1, 2),
    useCORS: true,
    logging: false
  });

  const image = canvas.toDataURL('image/jpeg', 0.92);
  const ratio = Math.min(maxWidth / canvas.width, maxHeight / canvas.height);
  const width = canvas.width * ratio;
  const height = canvas.height * ratio;
  const x = (pageWidth - width) / 2;
  const y = margin;

  if (index > 0) pdf.addPage();
  pdf.addImage(image, 'JPEG', x, y, width, height);
}

pdf.save('combined-sections.pdf');

The first section uses the PDF’s initial page; later sections call addPage(). If your installed jsPDF version exposes different internals or signatures, check that version’s official API documentation before shipping. The important sequence is library-independent: select, await, convert, place, add a page, then save.

Capture all canvases before writing the PDF

Sequential capture is easiest to reason about and limits simultaneous memory use. If capture time matters, you can start promises together, but large canvases may consume substantial memory. Do not call pdf.save() until every promise has resolved and every image has been added.

Put several sections on one page

Replace the unconditional page insertion with a vertical cursor. After calculating a section’s displayed height, place it at y; when y + height exceeds pageHeight - margin, call addPage() and reset y to the top margin. Keep the same width and aspect-ratio calculation shown above. If an individual element is taller than one page, split its source layout into smaller elements or accept that it must be scaled down; an image cannot flow naturally across PDF pages without additional slicing logic.

Capturing one combined container instead

Wrap the sections in a parent whose CSS layout already matches the desired document, then capture that parent:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const container = document.querySelector('#invoice');
const canvas = await html2canvas(container, {
  windowWidth: container.scrollWidth,
  windowHeight: container.scrollHeight,
  useCORS: true,
  backgroundColor: '#fff'
});

Setting the window dimensions to the element’s scroll dimensions can help when content is clipped, but browser canvas limits vary. Very large width, height, or total pixel area can produce a blank or partially rendered result. For long reports, separate captures are usually safer.

Images, fonts, and browser security

Cross-origin images

An image loaded from another origin can taint the canvas. useCORS: true only helps when that image server sends a suitable Access-Control-Allow-Origin header. It cannot override the browser’s same-origin policy. If you control neither server, use a same-origin proxy that fetches the asset and serves it with appropriate CORS headers. Test the proxy’s permissions and caching rules rather than disabling browser security.

Iframes

Cross-origin iframe documents cannot be rendered because the browser prevents access to their contents. Same-origin iframe content can be traversed recursively. If the frame is third-party, capture the frame’s own page separately (with its permission) or redesign the export so the needed data is in your document.

Unsupported or changing CSS

Because html2canvas reconstructs the DOM, unsupported CSS, web-font loading, animations, and hover states can differ from what you see on screen. Wait until fonts and data are ready, pause animations, and apply an export-only class that sets stable colors and visibility. Mark unwanted nodes with data-html2canvas-ignore, or use the configuration’s ignore predicate.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quality, size, and performance controls

  • Scale: A higher scale improves detail but increases pixels, memory, and PDF size. Device-pixel ratio is a practical starting point; cap it for mobile devices or large sections.
  • Image format: PNG preserves sharp text and transparency but is larger. JPEG is smaller for photographic content and does not preserve transparency. Choose the format deliberately in toDataURL() and addImage().
  • Lazy content: Scroll or otherwise trigger lazy-loaded images before capture, then wait for them to finish. html2canvas cannot capture pixels that have not loaded.
  • Repeated exports: For long-lived applications, the configuration documents clearImageCache as a way to release image-cache state. Avoid enabling it while concurrent captures share that cache.
  • Text behavior: This workflow places raster images into the PDF. Do not promise searchable or selectable text unless your particular PDF implementation separately adds a text layer.

Failure diagnosis

The PDF is blank

  • Check that the selector returns elements and that each element has non-zero dimensions.
  • Look for a tainted canvas caused by a cross-origin image; fix response headers or proxy the asset.
  • Reduce scale and split an oversized container into smaller captures.
  • Wait for fonts, images, and application data before calling html2canvas.

The right or bottom edge is cut off

Capture the element’s actual scroll dimensions, remove restrictive overflow during export, and try matching windowWidth and windowHeight to those dimensions. If the resulting canvas becomes enormous, capture sections independently instead.

A chart, shadow, or layout looks different

That property may not be supported by html2canvas or may depend on a browser state such as hover. Use an export stylesheet with simpler, explicit CSS and freeze animation before capture.

One section fails while others succeed

Wrap each capture in a try/catch, record the failing selector, and continue only if omitting that section is acceptable. A single inaccessible image or cross-origin iframe can affect one element without affecting the rest.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo accepts a URL and returns a PNG, JPEG, WebP, or PDF from one API request. For a server-side PDF or image capture, call the API instead of maintaining browser automation:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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 documentation for PDF options, full-page capture, custom CSS and JavaScript, selectors, waits, cookies, headers, device presets, and other controls. Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, failed loads, and timeouts are not billed, and response headers identify the page verdict and billing status. Its MCP server gives Claude, Cursor, and other MCP clients take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Can I preserve a transparent background?

Yes, if the canvas and the PDF image path both preserve alpha. Set an appropriate transparent backgroundColor and use a format and PDF configuration that support transparency; JPEG does not.

Can I export elements that are hidden with display:none?

No useful pixels exist for html2canvas to reconstruct from a non-rendered node. Render the element off-screen or temporarily apply an export class that makes it measurable, then restore the original styles.

Does this produce a real browser screenshot?

No. html2canvas recreates the DOM as a canvas and supports only the CSS it understands. For a browser-rendered page captured remotely, use a screenshot service such as ScreenshotNeo.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I preserve a transparent background?

Yes, when the canvas and PDF image path preserve alpha; JPEG cannot store transparency.

Can I export elements hidden with display:none?

Not directly. Make the element measurable with an export-only style, capture it, then restore the original style.

Does html2canvas produce a literal browser screenshot?

No. It reconstructs the DOM into a canvas and supports only the CSS it implements.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.