Skip to content

html2pdf.js blank pages, broken page breaks and unselectable text: what causes each and how to fix it

Blank PDFs, text sliced at a page boundary, blurry output, vanishing backgrounds, and an error the moment you try to run it in Node. Almost every html2pdf.js complaint traces back to one design decision: it rasterizes your page into an image rather than converting it. Here is what that causes and what to do about each symptom.

§ Live demo

Runs in your browser. Nothing is uploaded.

Your PDF is downloading. Want this as one API call, with the page archived too?

html2pdf.js is the most downloaded client side HTML to PDF library in the JavaScript ecosystem, and it generates a very consistent set of complaints: pages that come out completely blank, text sliced in half at a page boundary, output that looks blurry when you zoom in, backgrounds that vanish, and a stubborn error the moment anyone tries to run it on a server. Almost every one of those traces back to a single design decision the library makes, and once you know what that decision is, each fix becomes obvious. Here is what is actually happening, and what to do in each case.

The short version

html2pdf.js does not convert HTML to PDF. It screenshots your HTML with html2canvas and pastes that screenshot into a PDF with jsPDF. The library documentation says so directly: it "renders all content into an image, then places that image into a PDF. This means text is not selectable or searchable, and causes large file sizes." Blank pages come from exceeding the browser maximum canvas size, cut off text comes from the way the library reflows content to fit a page, and blurry output comes from rasterizing at too low a scale. None of these are bugs you can configure away completely, because they are consequences of rendering to an image.

Why does html2pdf.js produce blank pages?

Because the document got too big for a canvas. html2canvas has to paint your entire element into one HTML5 canvas before jsPDF can slice it up, and browsers cap how large a canvas may be. Exceed that ceiling and the canvas does not throw a helpful error, it silently returns empty, so you get a PDF with the right page count and nothing on any of them. The library issue tracker describes exactly this: large documents result in PDFs rendering completely blank.

The practical threshold varies by browser and by device memory, which is why this is the failure that reproduces on one machine and not another, and why it tends to appear in production on mobile Safari after passing every desktop test. A twenty page report is well inside the danger zone.

Three things help. Lower the html2canvas.scale option, since the canvas dimensions are your element size multiplied by that scale, and a scale of 4 needs sixteen times the pixels of a scale of 1. Split the document and render it in chunks, calling the library once per section and merging afterwards. Or stop rasterizing altogether and render server side, which removes the canvas from the picture entirely.

Why is html2pdf.js cutting off text?

This is the reflow problem, and it is documented as a known limitation rather than a bug in your code. The library resizes the root element to fit onto a PDF page, which causes the content inside it to reflow. Your carefully measured layout is not what gets captured; a rescaled version of it is. Elements land in positions you did not lay out, and a line of text can end up straddling the boundary where one page image ends and the next begins, so the top half prints on page one and the bottom half on page two.

You cannot fix this by adjusting margins, because the reflow happens after your CSS has done its work. What does help is making the breakable units small and explicit. Keep table rows, cards and paragraphs as discrete blocks, tell the library never to split them, and give it permission to break in the gaps between them instead. That is what the page break options are for.

How do I avoid page breaks in html2pdf.js?

Use the pagebreak option, which reads standard CSS break properties. The library honors break-before, break-after and break-inside, and also supports a legacy class based mode using html2pdf__page-break. In practice the CSS route is cleaner:

html2pdf()
  .set({
    margin: 10,
    filename: 'invoice-8842.pdf',
    html2canvas: { scale: 2, useCORS: true, backgroundColor: '#ffffff' },
    jsPDF: { unit: 'mm', format: 'letter', orientation: 'portrait' },
    pagebreak: { mode: ['css', 'legacy'], avoid: ['tr', '.card', 'h2'] },
  })
  .from(document.getElementById('report'))
  .save();

The avoid array is the one that solves most complaints. Listing tr stops table rows being sliced through the middle, and adding your card or section class keeps visual blocks intact. To force a break at a specific point, put break-before: page in your CSS on that element, or add the html2pdf__page-break class to an empty div where you want the split.

Set mode explicitly. Left to its default the library will not necessarily apply the CSS rules you wrote, and a page break configuration that silently never runs is a common reason people conclude the option does not work. If you want the broader treatment of this problem across every rendering engine, the HTML to PDF page breaks guide covers the CSS that survives each one.

Why is my html2pdf.js text not selectable?

Because there is no text in the file. This is the single most misunderstood thing about the library, and it is not a setting you have missed. html2pdf.js rasterizes your content into an image and places that image on the PDF page, so the finished document contains pixels and nothing else. You cannot select it, you cannot search it, screen readers cannot read it, and Google cannot index it if you publish it.

The knock on effects are real. File sizes run several times larger than a vector PDF of the same document, since you are storing a picture of text rather than the text and a font reference. Zooming reveals softness, because the image was captured at a fixed resolution. And anything downstream that expects to read the document, an e-signature platform, a document management system, an accessibility audit, sees an empty page. If you have already accumulated a library of image only PDFs from a tool like this, getting structured content back out of them means running them through document data extraction, which is a considerably more expensive way to arrive at text you originally had in the DOM.

Raising html2canvas.scale to 3 or 4 makes the image sharper and postpones the blurriness complaint, but it multiplies file size and pushes you toward the blank page problem described above. It buys quality at the cost of the other two failure modes. Selectable text requires an engine that draws text as text, which means Chromium print output rather than a canvas screenshot.

Why is the background color missing?

html2canvas defaults to a transparent background, and jsPDF then places that transparent image on a white page, so light designs look fine and dark or tinted ones lose their fill. Set it explicitly with html2canvas: { backgroundColor: '#ffffff' }, or pass null if you genuinely want transparency preserved.

A related trap catches images and fonts loaded from another domain. html2canvas cannot read pixels from a cross origin resource unless the server sends permissive CORS headers, so logos on a CDN come out blank while everything local renders. Set useCORS: true and make sure the asset host actually returns Access-Control-Allow-Origin. If it does not, and you do not control that host, no client side library can render it and you have to proxy the asset yourself.

Can html2pdf.js run in Node.js?

No, and this is settled rather than debatable. The documentation states that html2pdf.js "will not run in Node.js, it must be run in a browser." It depends on html2canvas, which needs a live DOM, a real layout engine and a canvas element, none of which exist in a bare Node process. Installing it from npm gets you the package, not the ability to execute it server side, which is why html2pdf npm is such a common search followed by a wasted afternoon with jsdom.

If your PDF has to be generated on a server, in a queue worker, on a schedule or anywhere a user is not sitting in front of a rendered page, this library is the wrong category of tool. The options that do work server side are laid out on the HTML to PDF Node.js page: Puppeteer or Playwright if you are willing to run Chromium yourself, PDFKit or pdfmake if the document is built from data rather than markup, or a rendering API if you would rather not operate a browser.

What is the best html2pdf.js alternative?

It depends which of its constraints is hurting you, so match the replacement to the actual problem.

  • You need selectable text and correct page breaks, and you render server side. Use a Chromium based renderer. Puppeteer page.pdf() emits real vector text and honors print CSS properly, or a hosted PDF generator API does the same over one HTTPS call with no browser to install.
  • The document is a React component and you want a real PDF, not a screenshot. See React to PDF, which covers why the html2canvas based React wrappers inherit every limitation on this page.
  • The document is generated entirely from data. Skip HTML rendering. PDFKit or pdfmake draw the document directly, run anywhere, and produce small files with real text.
  • You must stay purely client side with no backend at all. Then html2pdf.js is close to your only option, and the job is managing its limits rather than escaping them. Cap scale at 2, set pagebreak.avoid, set an explicit background, and keep documents short.

When html2pdf.js is genuinely the right tool

It is worth ending on the fair version of this, because the library gets criticized for failing at a job it never claimed. html2pdf.js exists to put a working download button on a page the user is already looking at, with no server involved and no infrastructure to run. For a dashboard export, a printable summary, a certificate the user generated in the browser a moment ago, it does that in about four lines and costs nothing. Version 0.10.1 is stable and widely used for exactly this.

The mismatch appears when it gets promoted into a document pipeline: invoices customers keep, statements that have to be searchable, reports that must survive an accessibility review, anything produced on a schedule. Those need real text, reliable pagination and server side execution, and the library is documented as providing none of the three. Diagnosing the failures above is useful, but if you find yourself fighting all of them at once, the honest read is that the document has outgrown client side rasterization, and the fix is a different engine rather than a better configuration. The HTML to PDF in JavaScript guide walks the full range of options with working code for each.

Written by the team building Sitepdf, an HTML to PDF API that archives every page it renders. The in-browser converter is free to try; early access locks the launch pricing.

§ Early access

Get on the early-access list

The API opens to the list first, in order. Early access locks the planned launch rates for 12 months. No card required, launching soon.

Render + archive, one API