HTML page break for printing

Three properties cover almost every case: break-before to start a new page, break-inside to hold a block together, and orphans and widows to stop single lines stranding.

To force an HTML page break for printing, put break-before: page on the element that should start a new sheet, inside a print media query.

@media print {
  .new-page { break-before: page; page-break-before: always; }
}

Both property names are there on purpose. break-before is the current one and page-break-before is the older alias, still honoured, and cheap to keep.

A browser print preview where a table has been cut across two sheets, with three rows stranded on the second.
A browser print preview where a table has been cut across two sheets, with three rows stranded on the second.

The three properties that cover almost everything

Property Value Does
break-before page Starts this element on a new sheet
break-after page Starts whatever follows on a new sheet
break-after avoid Keeps this element with what follows
break-inside avoid Tries to keep this element on one sheet
orphans a number Minimum lines left at the foot of a page
widows a number Minimum lines carried to the next page

Everything else in the fragmentation specification is either unimplemented or applies to multi column layout rather than paper.

A print stylesheet that handles the usual report

@media print {
  h2 { break-after: avoid; }
  h2, h3 { break-inside: avoid; }
  table, figure, .card { break-inside: avoid; }
  thead { display: table-header-group; }
  tfoot { display: table-footer-group; }
  p { orphans: 3; widows: 3; }
  .page-break { break-before: page; }
}

Read it as five separate fixes. Headings stay with the text they introduce. Tables and figures stay whole. Table headers and footers repeat on every sheet. Paragraphs do not strand one line. And an explicit class exists for the breaks you place by hand.

The h2 { break-after: avoid; } line is the highest value rule in the block. A heading alone at the foot of a page is the most noticeable printing fault and the easiest to remove.

Why avoid is a request, not a rule

break-inside: avoid asks the browser not to split the element. If the element is taller than one sheet, there is nowhere else for it to go and it splits anyway.

That is why a forty row table with break-inside: avoid still breaks. The fix is not a stronger rule. It is letting the table break and making sure the header repeats, which display: table-header-group on the <thead> already does.

Two other situations defeat it. An element inside a flex or grid container fragments differently, and an ancestor with overflow: auto or hidden can stop fragmentation from reaching the child at all.

The same preview after the print rules. The table starts on a fresh sheet and its header repeats.
The same preview after the print rules. The table starts on a fresh sheet and its header repeats.

Placing breaks by hand

Automatic rules handle structure. Section boundaries usually need a deliberate break.

<section class="page-break">
  <h2>Appendix A</h2>
</section>

Two habits worth keeping. Put the class on the section rather than on the heading, so the whole block moves together. And do not put a break before the first section, which produces a blank first sheet.

For a document assembled from parts, one class on each part is easier to maintain than a rule targeting section + section.

What else changes on paper

A print stylesheet is rarely only about breaks. Four other things go in the same block.

  • Backgrounds are dropped. Browsers do not print background colours by default, so zebra striping disappears. Add -webkit-print-color-adjust: exact; if the tint carries meaning.
  • Navigation is noise. Hide menus, share buttons and cookie banners with display: none.
  • Links lose their targets. a[href^="http"]::after { content: " (" attr(href) ")"; } prints the address after the text.
  • Sticky positioning does nothing. A frozen table header has no meaning on paper. The thead repeat replaces it.

Print stylesheets covers the whole block, and page size covers the sheet itself.

Checking it without wasting paper

The browser print dialog shows the real pagination, including the page count and thumbnails of each sheet. That preview uses the same engine as the printer, so what it shows is what comes out.

Three things to read in it:

  1. Is any heading alone at the foot of a sheet? Add break-after: avoid.
  2. Did a table split with no header on the second part? The header row is in the tbody, not a thead.
  3. Is there a blank sheet? A break landed on an element that was already at the top of a page.
The browser print dialog showing page thumbnails and a page count for the report.
The browser print dialog showing page thumbnails and a page count for the report.

Open the file in the HTML file opener and print from there. It shows the page without your project's screen styles competing.

Printing versus sending the page

A page laid out for paper is not automatically the right thing to send. PDF versus a live page covers the trade: a PDF freezes the numbers, a page keeps them current.

The practical arrangement is both. Paste the HTML into a NOS document and it renders as written, print rules included, at an address of its own. Share, then Share link, then Create link gives a link that opens in one click.

Whoever needs paper prints from that page, and the print stylesheet you wrote is what they get. Edits keep the same address, so nobody is working from a stale printout without knowing it.

Questions people ask

How do I force a page break in HTML before printing?

Put break-before: page on the element that should start a new sheet, inside a print media query. The older property name is page-break-before: always, and including both is still worth doing for older engines.

How do I stop a table or image splitting across two pages?

Set break-inside: avoid on the element. Browsers treat it as a strong preference rather than a guarantee, so a block taller than one page will still split, because it has nowhere else to go.

Why do my page breaks work in Chrome but not when I save to PDF?

Saving to PDF from the browser uses the same print path, so the breaks should hold. If they do not, the rules are probably outside a print media query, or an ancestor has a layout that removes the element from normal flow, such as a flex or grid container or overflow: auto.

Can I repeat a table header on every printed page?

Yes, and it is the default. A table with a proper thead repeats its header on each page a long table spans. If it is not happening, the header row is probably in the tbody rather than in a thead element.

Keep reading