Pagination is the part of DOM-to-PDF conversion that most affects the final experience: where does content break when it exceeds one page? Does the table header survive a page break? How do you force a chapter to start on a fresh page? Handled poorly, exported PDFs end up with orphaned headings, tables sliced in half, and scrambled chapter order — and the document instantly looks unprofessional. dompdf.js ships an automatic pagination engine that creates new pages as soon as content exceeds the page size, and it supports @page paged media properties for fine control over margins, forced breaks, and cross-page behavior. This guide explains the pagination principles and shows the common controls — auto pagination, forced breaks, and table handling — with complete code examples. The guide focuses on the mental model first — how the engine decides where a page ends — because every control, from margins to forced breaks, is easier to use once the mechanism is clear. All the techniques shown here are standard CSS, so the knowledge transfers to other paged-media contexts, and templates stay readable by any developer who knows CSS. Whether you are exporting a two-page invoice or a two-hundred-page manual, the same principles apply; the examples scale from the simplest case upward.
Long documents — contracts, reports, product manuals — exceed one page as a matter of course, and pagination quality directly determines how professional they look. Orphaned headings, truncated tables, and misplaced footers all read as sloppy, and they shape the first impression clients and leadership get. Pagination quality is also a trust signal in client-facing documents: a contract with a heading stranded at the bottom of a page reads as carelessness, however sound the content.
In print scenarios pagination matters even more: the printed result must match the on-screen preview. Poor break control causes problems with paper tray selection, duplex printing, and binding, and redoing a print run is expensive. In long-form documents, good breaks also aid comprehension: readers follow arguments more easily when related content stays together and chapters start cleanly.
Good pagination is smart: content flows naturally, important blocks stay intact where possible, and table headers repeat across pages. That is precisely the territory of dompdf.js's automatic pagination engine — the developer simply focuses on the content itself. The effect is strongest in mixed media: a PDF that paginates well looks as considered as a professionally typeset document, while a poorly broken one betrays its automated origin.
Cost is another angle: printing a report with broken pagination wastes paper and time, and re-running a print job for a client-facing deck is an embarrassment no team wants to explain.
Pagination also affects digital reading: a PDF that opens with a half-empty page or a sliced table is harder to annotate and reference, which matters in review workflows.
Designers often treat pagination as an afterthought, but it is the difference between a document that looks engineered and one that looks assembled — and it is controllable with a few CSS rules.
dompdf.js lays out the incoming HTML or DOM against the page size and creates a new page automatically when content overflows. Text, images, and tables are all handled correctly at page boundaries — nothing overlaps and nothing is lost. The engine measures content against the page's content box — the area inside the margins — so changing margins changes where breaks land; the two settings are always considered together.
Pagination stability is directly tied to container width: A4 corresponds to 794px at 96 DPI. Matching the export container's CSS width to the target paper size yields the most stable and predictable page breaks. Because pagination is deterministic for a given input, the same template and data always produce the same document, which makes QA reproducible instead of flaky.
Layout computation runs in a Web Worker, so the main thread is never blocked while exporting large documents — the interface stays responsive, and progress can be surfaced through callbacks so users see accurate feedback during long exports. Overflow handling is uniform across element types: a paragraph continues on the next page, an image moves as a block, and a table resumes at the row boundary.
The 794px figure deserves a closer look: it comes from converting 210mm at 96 DPI. Keeping the export container at that width makes the browser's layout match the PDF's layout, which is why break behavior becomes predictable.
The Web Worker execution has a practical consequence: export can run while the user keeps interacting, and long documents can show a progress bar instead of freezing the tab.
If a document's pagination looks wrong, the fastest diagnostic is to narrow the container to the paper width and re-export; most 'random' breaks trace back to width mismatches.
A useful mental shortcut: pagination is a consequence of layout, not a separate step — anything that changes layout (fonts, widths, margins, image sizes) changes breaks, so treat the template and the page settings as one system.
The @page rule sets paper size and margins globally; page-break-before: always forces a chapter onto a new page, and page-break-inside: avoid protects important blocks from being split. Together these two cover most real-world pagination needs. The page rule applies to the document as a whole, while the break properties are per-element; combining both gives global defaults plus targeted exceptions.
All of this is standard CSS paged media, declared in a style tag inside the template — no extra JavaScript logic required. Templates stay cheap to maintain, and designers can tune the breaks directly. Because the rules live in the template's style tag, designers can experiment with break behavior without touching JavaScript, which shortens iteration loops.
break-before and break-inside are the modern equivalent spellings and both work. When combining them with tables and images, fine-tune based on the rendered result — the final PDF is the source of truth. The example intentionally keeps the document short so the effect of each rule is visible in the output; adding more content simply demonstrates the same rules at scale.
page-break-before: always is the workhorse for chapter starts, but it also works for appendices, annexes, and any block that deserves a fresh page; the rule simply means 'start on a new page'.
page-break-inside: avoid is equally simple in meaning — 'do not split this block' — and applying it to headings, figures, and table rows prevents the most common visual defects.
A practical pattern for reports: keep headings attached to their first paragraph by wrapping both in an avoid-splitting container, and let long body text flow freely across pages.
When a document must always start on an odd (right-hand) page for duplex printing, the forced-break rules plus page counting handle the insertion of blank pages automatically, matching the conventions of bound books.
import { DomPDF } from 'dompdf.js';
const html = `
<style>
@page { size: A4; margin: 20mm 15mm; }
.chapter { page-break-before: always; }
.keep-together { page-break-inside: avoid; }
</style>
<h1>Product Manual</h1>
<p>Chapter 1: Introduction. Content that should stay together where possible...</p>
<div class="chapter">
<h2>Chapter 2</h2>
<p>Chapter 2 starts on a fresh page, clearly separated from the previous chapter...</p>
</div>
<div class="keep-together">
<h3>A block that must not be split</h3>
<p>This heading and this paragraph must stay on the same page to avoid an orphaned heading...</p>
</div>`;
const pdf = new DomPDF();
pdf.addPage(html, { format: 'A4', margin: '20mm' });
pdf.save('manual.pdf');
Tables are the worst pagination offender: rows taller than the remaining space get cut, and a lost header makes later pages hard to read. dompdf.js has dedicated support for table borders and cross-page rendering, so long tables can span pages continuously. Table headers repeating across pages is handled by the engine, so multi-page tables stay readable without any per-page markup — one of the biggest quality wins over naive splitting.
Practical advice: use percentage column widths and avoid oversized images inside rows. For important tables that must appear complete, apply page-break-inside: avoid so the whole table moves to a new page. Percentage column widths keep tables responsive to the page width, which is more robust than fixed pixels when margins change.
For block-level content — cards, code blocks, chart containers — set avoid-splitting consistently, and combine it with the "each chapter starts on a new page" rule. Long documents become noticeably easier to read, with clear page structure. The same cross-page rules that protect tables protect code blocks and card grids, so one set of CSS conventions covers most long-content layouts.
Rows themselves are treated as atomic by default, so a single row is not cut in half; if a row is taller than a full page, the engine still splits it gracefully rather than dropping content.
For tables that must never split, page-break-inside: avoid moves the entire table to the next page — the right trade-off when a table is short enough to fit and important enough to stay whole.
Images inside table cells need extra care: an oversized image forces the row to grow and can push the table across pages unexpectedly; scale images to a column-appropriate width before inserting them.
For wide tables that overflow the content box, consider rotating them to landscape inside the portrait document; combined with forced breaks, this keeps data readable without shrinking text to illegibility.
Multi-page documents usually need headers and footers: the document title in the header and "Page X of Y" in the footer. dompdf.js supports headers, footers, and page numbers that update automatically as pagination proceeds — no per-page handling. Because headers and footers are generated at layout time, they respect the same pagination rules as body content — a footer never overlaps body text because the margins reserve its space.
Page numbers increment continuously across pages. Cover pages and table-of-contents pages can be excluded from numbering so the body starts at page 1, matching the conventions of formal documents. The page counter is a native feature, so 'Page X of Y' stays correct even when content shifts after an edit, with zero manual bookkeeping.
Combining pagination, headers and footers, and @page means you can produce a formally structured PDF with consistent styling — without writing a single coordinate-based layout instruction. Headers and footers also give the reader orientation: after a page break, the header names the document and the footer names the position, so any page stands alone.
Numbering exclusions for cover pages are configured declaratively, so the body can start at page 1 while the cover remains unnumbered — the standard convention for formal documents.
A common combination is a title header with a page-number footer; the two are independent margin areas, so each can be styled differently without conflict.
When a document is assembled from several addPage calls, pagination and numbering run continuously across all of them, which keeps multi-source documents coherent.
Page numbers that restart per chapter are a documented convention in long manuals; the counter controls make per-chapter restart possible, giving multi-part documents the structure readers expect.
Q: Why do my page breaks differ from the browser's print preview? A: Pagination depends strongly on container width and page size. First align the container width to the paper's pixel width (794px for A4), then compare again. Also check that the export area has no horizontal overflow; a scrollbar-width difference is a classic cause of break mismatches between preview and PDF.
Q: Can images be cut off at a page break? A: Images are treated as atomic blocks and are not split by default. Very large images should be scaled to a sensible width before insertion for the most stable result. If a large image must appear in full, place it in an avoid-splitting block; the engine will push it to the next page rather than crop it.
Q: How do batch addPage calls combine with auto pagination? A: Each addPage's content is laid out independently, and overlong content paginates automatically. In batch scenarios the two mechanisms stack cleanly without conflict. When comparing against browser print preview, remember that previews apply their own default margins — align both sides to the same page settings before judging the result.
Q: Do forced breaks interfere with auto pagination? A: No — forced breaks override the automatic flow for the elements they target, and auto pagination continues normally for everything else; the two mechanisms compose cleanly.
Q: What about landscape pages inside a portrait document? A: The page rule can specify orientation per page group; combined with forced breaks, a report can contain mixed-orientation sections without manual assembly.
Q: Why does a very long paragraph sometimes leave a single line on the next page? A: The engine respects avoid-splitting rules and can push a line to keep a block together; if the widow bothers you, wrap the paragraph in a block with the appropriate break rules.
Q: Do images count toward pagination the same way as text? A: Yes — images occupy layout space like any block; the difference is that they move as atomic units, so plan image sizes relative to the content box.
下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:
这是由 dompdf.js 渲染的示例 PDF 内容。