How to Fix jsPDF html() Splitting Elements Incorrectly
Fix jsPDF html() pagination problems by matching the renderer, configuring autoPaging or pagebreak rules, and testing the exact versions and CSS.
Direct answer: first identify which renderer you are using. Direct jsPDF.html(), the older fromHTML() API, and the html2pdf.js wrapper have different pagination controls. With current jsPDF, try autoPaging: "text" for text-aware splitting, or set autoPaging: false when you need to place content and add pages yourself. If you use html2pdf.js, configure its separate pagebreak option with CSS rules and explicit selectors.
These settings are best-effort. jsPDF’s HTML plugin uses html2canvas, so it does not reproduce the browser’s print layout for every CSS property. Large cards, tables, images, transforms, and custom layout boxes can still split. The reliable fix is a small reproduction tested against the exact package versions, browser, page size, margins, and markup in your application.
1. Identify the PDF path before changing options
| Code path | Pagination controls | Typical fix |
|---|---|---|
doc.html(element, options) |
jsPDF’s autoPaging, position, margins, and html2canvas options |
Try autoPaging: "text"; use false for manual layout |
html2pdf().set(...).from(element) |
html2pdf.js pagebreak modes and selector lists |
Use css/legacy modes and targeted avoid selectors |
Legacy fromHTML() |
Older implementation and issue history | Do not copy html() advice without checking the installed release |
Record the versions of jspdf, html2canvas, and html2pdf.js (if present), plus browser and operating system. Current project documentation can describe a newer branch than your lockfile.
2. Fix direct jsPDF html() pagination
Start with text-aware paging
const doc = new jsPDF();
const content = document.querySelector("#content");
doc.html(content, {
autoPaging: "text",
callback: (pdf) => pdf.save("output.pdf")
});
Current jsPDF typings expose autoPaging as true, false, "slice", or "text". When omitted, the current plugin initializes it to true. Check the version installed in your project before relying on that default.
Use manual pagination when boundaries must be deterministic
const doc = new jsPDF({ unit: "pt", format: "a4" });
const pageWidth = doc.internal.pageSize.getWidth();
const margin = 36;
const sections = [...document.querySelectorAll(".pdf-section")];
let y = margin;
for (const section of sections) {
const canvas = await html2canvas(section, { backgroundColor: "#ffffff" });
const ratio = (pageWidth - margin * 2) / canvas.width;
const height = canvas.height * ratio;
if (y + height > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
y = margin;
}
doc.addImage(canvas, "PNG", margin, y, pageWidth - margin * 2, height);
y += height + 18;
}
doc.save("output.pdf");
Manual placement gives you control over where sections start, but it turns text into canvas images and requires your code to measure every block. It is useful for fixed cards or reports where a split is unacceptable.
Useful html() options
| Option | Purpose | Practical note |
|---|---|---|
autoPaging |
Controls automatic page creation | "text" is a starting point for line-aware text flow; false enables manual placement |
margin |
Sets PDF margins | Increase it when content is being clipped at the page edge |
x, y, width |
Positions and scales the rendered HTML | Changing width changes line wrapping and therefore page breaks |
windowWidth |
Sets the virtual browser width used by html2canvas | Match the CSS breakpoint you want to capture |
html2canvas |
Passes renderer settings through | Use it for scale, background, and resource-loading behavior |
callback |
Runs after rendering | Save or post-process the completed document here |
3. Fix html2pdf.js page breaks
html2pdf.js adds its own page-break layer over jsPDF and html2canvas. Its documented controls do not belong to direct doc.html().
html2pdf()
.set({
pagebreak: {
mode: ["css", "legacy"],
before: [".new-page"],
after: [".end-page"],
avoid: [".keep-together"]
}
})
.from(document.querySelector("#content"))
.save("output.pdf");
Use CSS break rules on meaningful blocks
.new-page {
break-before: page;
}
.keep-together {
break-inside: avoid;
}
.end-page {
break-after: page;
}
The CSS mode recognizes break-before and break-after values such as always, left, and right, plus break-inside: avoid. Start with specific selectors. avoid-all attempts to keep every element together, which can create large blank areas or push content farther than expected.
4. Why break-inside: avoid can still fail
- The block is taller than a page. A renderer cannot keep a block together if it cannot fit on one page; it must split or overflow.
- The renderer is canvas based. html2canvas does not implement every CSS feature, so browser print behavior is not a guarantee.
- Width or margins changed wrapping. A one-line change can move a heading or table row to the next page.
- Ignored nodes changed geometry. Elements marked for exclusion can still affect how a wrapper calculates breaks in some cases.
- Images or fonts loaded late. Measurements taken before resources finish loading produce stale heights.
- Transforms and positioned elements confuse measurement. Visual position and document flow can diverge.
- You copied options between libraries. html2pdf.js
pagebreaksettings are ignored by direct jsPDFhtml().
5. A reproducible diagnosis workflow
- Capture the environment. Save dependency versions, browser, OS, page format, margins, viewport width, and whether you pass a DOM node or an HTML string.
- Reduce the DOM. Keep only the smallest subtree that still produces the bad split. Preserve computed dimensions and relevant CSS.
- Freeze inputs. Use the same browser, URL or fixture data, fonts, image files, page size, and margins for every comparison.
- Change one layer. For direct jsPDF, compare the default with
autoPaging: "text", thenfalseif manual layout is possible. For html2pdf.js, compare CSS mode and one explicitavoidselector. - Test edge cases separately. Check a block shorter than a page, exactly page-sized, slightly taller than a page, and a document with a large image or table.
- Inspect the output. Keep the generated PDF and screenshots showing the expected and actual boundary. This makes regressions reviewable.
- Escalate with a focused reproduction. Include markup, CSS, options, versions, browser, margins, page format, and the smallest failing example.
6. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| A line is cut between pages | Automatic slicing or canvas boundary | Try autoPaging: "text"; for strict control, render sections and add pages manually |
A card splits despite break-inside: avoid |
Wrong renderer, oversized card, or unsupported CSS | Confirm html2pdf.js versus jsPDF; target the card with avoid; redesign blocks taller than a page |
| Large blank gaps appear | avoid-all or broad avoid selectors |
Replace broad rules with selectors for cards, figures, or table groups |
| Breaks occur after hidden or ignored content | Pagination and canvas visibility differ | Remove ignored nodes from the reproduction and test the exact data-html2canvas-ignore usage |
| Images overlap or move after page breaks | Images were not loaded before measurement | Wait for image promises and fonts before calling the renderer; use stable dimensions |
| CSS works in Chrome print but not in PDF | html2canvas CSS support differs from the browser print engine | Replace unsupported layout with simpler flow, or choose a renderer that matches your CSS requirements |
pagebreak appears to do nothing |
Options were passed to direct jsPDF | Use html2pdf.js for those options, or switch to jsPDF’s autoPaging and manual placement |
| Text is missing from an HTML string | Sanitization or dependency setup | Use a DOM node where possible and verify the DOMPurify dependency required by the jsPDF HTML plugin |
7. Tables, images, and long documents
Tables
Keep table headers in a dedicated block and split very large tables into groups that can fit on a page. Avoid relying on one CSS rule to keep an entire multi-page table together. Verify row height after fonts and images load.
Images
Set explicit width and height, wait for loading, and test an image that is near the remaining page height. A single image taller than the printable area must be scaled or split.
Long reports
Canvas rendering consumes memory proportional to the rendered pixel area. Reduce capture width or scale, render sections independently, and release intermediate canvases. Compare output size and selectable text requirements before choosing a canvas-heavy workflow.
8. Performance, reliability, and cost considerations
- Performance: large DOM trees, high device scale, web fonts, and full-page images increase rendering time and memory use. Measure with the same content users submit.
- Reliability: wait for fonts, images, and application data; disable animations; use deterministic CSS dimensions; and retry only when the failure is transient.
- Output quality: higher canvas scale improves visual sharpness but increases memory and PDF size. It does not make unsupported CSS work.
- Pagination: treat page-break rules as renderer-specific. A browser screenshot, browser print PDF, jsPDF canvas output, and html2pdf.js output can all differ.
- Cost: local libraries have no API request charge, but they use your browser or server CPU and memory. A hosted renderer trades setup and maintenance for a per-capture plan; compare based on volume, CSS fidelity, and operational limits.
9. Or skip the browser setup
If your goal is a dependable capture rather than debugging a local canvas pipeline, ScreenshotNeo provides a single API request for PNG, JPEG, WebP, or PDF. It accepts 63 capture options, including full-page lazy-image loading, CSS-element capture, custom CSS and JavaScript, waits, blocking rules, headers, cookies, user agents, timezone, geolocation, PDF margins and page ranges, caching, signed links, async jobs, bulk capture, and a usage API. See the ScreenshotNeo API documentation for the current parameter names.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-d format=pdf \
-o page.pdf
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://example.com",
"format": "pdf",
},
timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({
access_key: "YOUR_API_KEY",
url: "https://example.com",
format: "pdf"
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await Bun.write("page.pdf", bytes);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response reports the result with X-Page-Verdict and X-Billed headers. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account and start with 1,000 screenshots per month at no charge.
10. FAQ
Does autoPaging: "text" keep every element together?
No. It targets text-aware paging. Arbitrary cards, images, tables, and custom boxes can still split, especially when they exceed a page or use unsupported CSS.
Should I use autoPaging: false for every report?
Use it when you can measure and place sections yourself. It provides control but requires manual layout code and usually renders sections as images.
Can html2pdf.js avoid selectors fix direct jsPDF output?
No. Those selectors belong to html2pdf.js. Direct jsPDF uses its own html() options.
Why does the same HTML split differently after a dependency update?
Pagination depends on renderer versions, CSS support, browser behavior, fonts, margins, and width. Pin versions and keep a minimal PDF fixture for regression checks.
When should I choose another renderer?
If you require browser print fidelity, semantic selectable text, repeated headers, or strict page-break guarantees, compare a renderer whose pagination model supports those requirements. Make that decision from a reproduction and your document constraints.


