DynamicDocumentAPI

Pagination basics

Control page breaks, keep blocks together, repeat table headers and fix the usual pagination problems in HTML to PDF output.

View as Markdown

A PDF is your HTML poured into fixed-size pages. Chromium decides where the content breaks, and CSS fragmentation properties let you steer those decisions. This page collects the rules that matter for documents, and the fixes for the problems that come up most often.

The page box comes from the PDF options: paper, orientation and margin, or from your CSS @page rule when prefer_css_page_size is on.

Force a page break

Break before or after an element with CSS:

.chapter { break-before: page; }
.cover { break-after: page; }

Or insert a break from the template, which is easier inside loops:

{% for certificate in certificates %}
  <section class="certificate">…</section>
  {% if not loop.last %}{{ page_break() }}{% endif %}
{% endfor %}

Note the loop.last check: a break after the final element produces an empty last page.

Keep content together

break-inside: avoid moves an element to the next page instead of splitting it:

.totals,
.signature-block,
figure,
tr {
  break-inside: avoid;
}

An element taller than one page will still be split: there is nowhere else to put it.

Keep a heading with its content

break-after: avoid asks for no break directly after an element:

h2, h3 { break-after: avoid; }

The most reliable approach is to wrap the heading and the first block that must stay with it:

<div style="break-inside: avoid">
  <h2>Payment details</h2>
  <p>Please transfer the total to the account below within 30 days.</p>
</div>

Tables

Long tables are the main source of pagination problems. Three rules cover most of it:

thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr { break-inside: avoid; }
  • thead repeats the header row at the top of every page the table spans. Put the header cells in a real <thead> element, not in the first <tbody> row.
  • A tfoot group repeats at the bottom of every page, so keep grand totals in a block after the table if they should appear only once.
  • tr { break-inside: avoid; } keeps a row's cells on one page. Rows with a lot of text can still break if a single row is taller than the page.

Orphans and widows

Stop single lines being left behind at the bottom or top of a page:

p { orphans: 3; widows: 3; }

Page size in CSS

By default the paper, orientation and margin options define the page. With prefer_css_page_size: true, the size from your CSS wins, which also allows different sizes within one document using named pages:

@page { size: A4; }
@page wide { size: A4 landscape; }

.appendix-table { page: wide; }

Keep setting margins through the margin option, so that headers and footers and your margins stay in sync.

Single-page output

single_page: true produces one page as tall as the content, with the paper width you set. It is the usual choice for receipts on narrow paper and for continuous documents that will be read on screen:

{ "paper": { "width": "80mm", "height": "200mm" }, "single_page": true }

Common problems and fixes

SymptomLikely causeFix
Table rows split across pagesRows may break by defaulttr { break-inside: avoid; }
Header row only on the first pageHeader cells are in tbody, or display was overriddenUse <thead> with display: table-header-group
Empty page at the endA break after the last element, or a full-height elementSkip the break for the last item, avoid height: 100vh and fixed full-page heights
Heading alone at the bottom of a pageNo keep-with-next rulebreak-after: avoid, or wrap heading and first paragraph in a break-inside: avoid container
Body text hidden behind the header or footerMargin smaller than the header or footer heightIncrease margin.top or margin.bottom; check the render's warnings
Background colours or images missingBackgrounds aren't printedSet print_background: true and print-color-adjust: exact on the element
The PDF looks different from the browserPrint styles are appliedCheck your @media print rules, or render with emulate_media: "screen"
Wrong or fallback fontsThe font isn't bundled with the engine, or its file couldn't be loadedUse the engine's bundled fonts, or declare @font-face in the head with a reachable source. The browser falls back without a warning, but a font file that couldn't be loaded shows up as an asset_failed warning
Content cut off on the rightFixed widths wider than the printable areaPrintable width is the paper width minus both margins, for example 180 mm on A4 with 15 mm margins. Use percentages and box-sizing: border-box.
Flex or grid sections break oddlyFragmentation of flex and grid containers is limitedUse block layout or tables for long, paginated content
Unexpectedly many pagesLong data, oversized images or forced breaksCheck the render's pages count; a PDF over your plan's page limit fails with page_limit_exceeded

Page numbers

Page numbers belong in the header or footer, where the renderer fills in the current and total page numbers. See Headers and footers.

Pagination toolkit

Renders of code and Markdown templates, and of HTML or Markdown you send, load a small pagination stylesheet that covers the same ground with less CSS. Pages rendered from a URL don't get it.

Print defaults. Table header and footer groups repeat on every page. Table rows, images, SVGs and figures aren't split. Paragraphs, list items and quotes keep at least three lines at the bottom and top of a page, and headings stay with what follows them. The defaults sit in a CSS cascade layer, so any rule of your own overrides them.

Utility classes. Add them to elements in your template:

ClassEffect
.dda-keep-togetherDon't split the element across pages
.dda-keep-with-nextNo page break directly after the element
.dda-page-break-before, .dda-page-break-afterStart a new page before or after the element
.dda-avoid-row-split, .dda-allow-row-splitOn a table or a row: keep rows on one page, or let them break
.dda-repeat-header, .dda-no-repeat-headerOn a table: repeat the header row on every page, or print it once
.dda-orphans-widowsKeep at least three lines at the bottom and top of a page
.dda-print-only, .dda-screen-onlyShow the element only in print, as in PDFs, or only on screen
.dda-fill-pageFill the remaining page height, for example to push a signature block to the bottom

Pagination check. The editor's PDF preview flags table rows and images split across pages, tables that continue without their header, table footers such as totals that print on every page, headings at the bottom of a page, elements wider than the printable area, unbreakable blocks taller than a page, and blank or nearly blank last pages. Most findings come with a one-click fix that adds CSS to the template's head.

Still planned:

  • .dda-running-title, which repeats the current section's title in the header
  • an optional paged-media mode for features Chromium lacks, such as running elements, tables of contents with page references, and footnotes

On this page