October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Canvas

How to Split Pages After Adding Images with html2canvas and jsPDF

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

Capture the target element with html2canvas, convert the PDF page’s usable height into canvas pixels, and add one canvas crop per jsPDF page. The key is to keep units straight: canvas dimensions are pixels, while jsPDF coordinates use the document unit you selected, such as millimeters. The method below produces predictable fixed-height breaks; it does not automatically keep an image or other DOM block intact when a break falls through it.

How the page-splitting calculation works

html2canvas renders a DOM element to a canvas. The rendered canvas is an image, so jsPDF does not know where paragraphs, images, or other HTML blocks begin and end. To paginate it, calculate how many canvas pixels fit within the printable height of one PDF page, crop that many pixels, add the crop, and repeat for the remaining canvas.

For a portrait A4 page in millimeters, the page is 210 × 297 mm. With 10 mm margins on every side, the usable area is 190 × 277 mm. If the full canvas is scaled to the usable width, the source pixels per PDF millimeter are canvas.width / 190. Multiply that ratio by 277 to find the canvas-pixel height of a full page slice. This proportional conversion is what prevents millimeters from being mistakenly treated as pixels.

The last slice is usually shorter than the others. Its displayed height should be calculated from its actual pixel height, rather than stretched to fill the page.

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

Runnable JavaScript example: capture and split a DOM element

Install the libraries in a JavaScript project with a bundler, for example npm install html2canvas jspdf. Give the content you want to export a stable selector such as #report, then run the following in a browser context. The example uses portrait A4, millimeter units, and 10 mm margins.

import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';

async function saveElementAsPdf(selector) {
  const element = document.querySelector(selector);
  if (!element) throw new Error(`No element found for ${selector}`);

  // Wait for images in the target element before rendering.
  await Promise.all(
    Array.from(element.querySelectorAll('img')).map((img) => {
      if (img.complete) return Promise.resolve();
      return new Promise((resolve) => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });
    })
  );

  const canvas = await html2canvas(element, {
    windowWidth: element.scrollWidth,
    windowHeight: element.scrollHeight,
    useCORS: true,
    backgroundColor: '#ffffff'
  });

  const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
  const margin = 10;
  const pageWidth = pdf.internal.pageSize.getWidth();
  const pageHeight = pdf.internal.pageSize.getHeight();
  const usableWidth = pageWidth - 2 * margin;
  const usableHeight = pageHeight - 2 * margin;

  // Scale the full canvas to the printable width, then derive a page-height crop.
  const sourcePixelsPerPdfUnit = canvas.width / usableWidth;
  const sourceSliceHeight = Math.floor(usableHeight * sourcePixelsPerPdfUnit);
  if (sourceSliceHeight < 1) throw new Error('Calculated page slice is too small');

  for (let sourceY = 0; sourceY < canvas.height; sourceY += sourceSliceHeight) {
    const sliceHeight = Math.min(sourceSliceHeight, canvas.height - sourceY);
    const pageCanvas = document.createElement('canvas');
    pageCanvas.width = canvas.width;
    pageCanvas.height = sliceHeight;

    const context = pageCanvas.getContext('2d');
    if (!context) throw new Error('Could not create a 2D canvas context');
    context.drawImage(
      canvas,
      0, sourceY, canvas.width, sliceHeight,
      0, 0, canvas.width, sliceHeight
    );

    if (sourceY > 0) pdf.addPage();
    const renderedHeight = sliceHeight / sourcePixelsPerPdfUnit;
    pdf.addImage(pageCanvas, 'PNG', margin, margin, usableWidth, renderedHeight);
  }

  pdf.save('report.pdf');
}

saveElementAsPdf('#report').catch(console.error);

The image-waiting code resolves on either load or error so one failed image does not leave the export waiting forever. It does not make a cross-origin image readable to the canvas; the image host must still grant CORS access. If your page inserts images asynchronously after its initial load, wait for the application’s own ready state before calling the function.

Adjust page size, margins, and headers

Choose jsPDF’s orientation, unit, and format when constructing the PDF, then obtain page dimensions from pdf.internal.pageSize as in the example. Do not hard-code a pixel height for a page or subtract a millimeter margin from a pixel canvas height. Recalculate the usable dimensions and pixel-per-unit ratio whenever you change paper size, orientation, or margins.

  • Custom margins: set margin to the desired value in the chosen jsPDF unit. If left and right margins differ, calculate usable width as page width minus both margins and place each crop at the left margin.
  • Headers and footers: subtract their reserved heights from the available page height before calculating sourceSliceHeight. Draw header or footer text separately on each PDF page so it does not become part of the cropped content.
  • Landscape or another paper size: change the jsPDF options and use the resulting page width and height in the same calculation. The crop math itself does not depend on A4.
  • Retina or higher-resolution output: html2canvas has a scale option. A larger canvas can improve image detail, but it also increases memory use; because the crop ratio is derived from the resulting canvas width, the pagination calculation remains proportional.

Choose between fixed slices and content-aware breaks

Use fixed-height crops for simple, regular pagination

Canvas slicing is appropriate when a precise repeating page height matters more than keeping individual blocks together. The boundary is a horizontal line through the rendered pixels. If it crosses a small image, heading, or paragraph, that content will be divided between PDF pages. Moving a break by a few pixels may help in a particular document, but it is not a semantic pagination rule.

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

Measure DOM boundaries when blocks must stay intact

If images, cards, or other blocks must not split, identify their positions before rendering and choose page breaks at suitable element boundaries. Another option is to render separate DOM sections and place each section on a page, while handling a section taller than one page as a special case. This requires more layout logic, but it gives the application control over where breaks occur.

Use a different workflow when text must remain selectable

Adding canvas crops as images creates a rasterized PDF page. Text in the capture is part of the image, rather than selectable PDF text. If searchability, selectable text, or semantic document structure is essential, use a DOM-aware or text-based PDF workflow instead of treating one tall screenshot as the document. That recommendation follows from the image-based capture and placement approach; it is not a claim that either library is limited to this one workflow.

Images, cross-origin content, and capture timing

Set useCORS: true when remote images are served with the required Access-Control-Allow-Origin response header. This option asks the browser to load images in a CORS-enabled way; it cannot override a server that does not grant permission. A same-origin proxy is an alternative when you control the application and can safely serve the asset through your own origin. See the html2canvas FAQ for its guidance on CORS and canvas sizing.

A capture can also miss content that has not been rendered yet. Wait for application data, fonts, and lazy-loaded images to settle before capturing. If the capture includes content below the visible viewport, supplying the element’s scrollWidth and scrollHeight as windowWidth and windowHeight can help html2canvas render against the element’s full dimensions, as described in the project FAQ. The html2canvas examples show its element-to-canvas workflow.

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

Large documents: memory and reliability

One very tall canvas is convenient, but browser canvas limits vary, and a large canvas may be blank or truncated. The html2canvas FAQ explicitly warns that a canvas may hit browser size limits; it does not give one universal numeric maximum. Increasing scale makes this problem more likely because both canvas dimensions and memory needs grow.

  • For long pages, capture bounded sections rather than building a single extreme-height canvas. Preserve the same width and scale across sections if their output should align.
  • Release temporary canvases when they are no longer needed. In a long-running application, assigning width = 0 and height = 0 to an unused canvas can release its backing storage sooner.
  • For a document with many pages, test the largest realistic input on the browsers and devices your users rely on. The available canvas limits are browser-dependent, so one successful test does not establish a universal safe size.
  • Test page boundaries where images and other important blocks occur. A technically correct pixel crop can still produce an awkward document if the break is visually unfortunate.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common split and export problems

Symptom Likely cause Fix
Pages are too tall, too short, or overlap Canvas pixels and PDF units were mixed, or margins were omitted from the usable dimensions. Derive source pixels per PDF unit from canvas width divided by usable PDF width, then multiply by usable PDF height for the crop size.
Content is clipped at the bottom or missing The capture window did not cover the element’s full scroll dimensions, or the canvas hit a browser size limit. Set windowWidth and windowHeight from the target element’s scroll dimensions. For very long content, capture smaller sections.
A remote image is missing or the canvas cannot be used The image origin did not allow CORS access. Use useCORS: true only when the remote server sends the required permission header, or serve the asset through a same-origin proxy.
An image or text block is cut between pages The crop is a fixed pixel boundary, not a DOM-aware page break. Move the boundary to a measured content break or render DOM sections separately.
Only part of the page appears in the PDF The capture occurred before lazy content, application data, or images were ready. Wait for the page’s own rendering process and required assets before calling html2canvas.
Export is slow or the tab becomes unresponsive A very tall or high-scale canvas consumes substantial browser memory. Lower the scale if detail permits, reduce the captured area, or capture bounded sections and add them incrementally.

Or skip the browser setup

If you need a website screenshot rather than a custom client-side DOM-to-jsPDF pagination routine, ScreenshotNeo returns a screenshot from one GET request. It is not a drop-in replacement for the page-splitting code above: use the DIY method when you need to choose page breaks in your own captured DOM.

For example, this cURL request saves a screenshot of Stripe as WebP:

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 API details. Cookie banners are accepted and removed before capture, along with 60+ known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan.

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.

Sign up for 1,000 free screenshots a month, with no card required.

References for the implementation

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.