Print Tables: Controlling Page Breaks Deliberately in CSS
AI generated
{ }
@
CSS · Print · Tables · Page Breaks
Print tables: controlling page breaks deliberately
Keeping a long order list readable across multiple pages, without tearing it apart

A long table breaking mid-row when printed is one of the most visible CSS bugs on invoices and order confirmations. With break-inside: avoid for rows and a correctly used thead as a repeating header, page breaks can be controlled deliberately so every page stays readable on its own, with no JavaScript or server-side PDF rendering required.

15 min read break-inside · thead repeat Invoices & order confirmations

1. The core problem: why tables tear apart when printed

By default, browsers break a page exactly where the available space runs out, with no regard for whether that point falls in the middle of a table row. For an order list with many line items, that can mean, in the worst case, that item name, quantity and price of a single row end up split across two different pages, while the matching column headers stay visible only on the first page.

This behavior is not a browser bug but the plain default without explicit control: the print algorithm has no automatic knowledge of the logical relationship between the cells of a row, it only sees a continuous sequence of boxes that it breaks at the next best opportunity. Only deliberate CSS rules tell the browser which areas must stay together as a unit.

2. break-inside: avoid for table rows

The property break-inside: avoid, applied to tr, instructs the browser to avoid a page break inside a table row wherever possible, instead moving the entire row to the next page if it no longer fits completely on the current one. For the vast majority of rows with normal height, this works reliably across every modern browser and resolves the core problem of torn rows almost completely.

For unusually tall rows, for example with multi-line free text in one column, avoid can hit physical limits: if the row itself does not fit on a full page, the browser still has to break somewhere. In practice this rarely affects normal order line items, but for free-text fields like delivery notes, deliberately capping the row height in the print stylesheet is worth doing.


@media print {
  table {
    border-collapse: collapse;
    width: 100%;
  }

  /* Keep rows from being torn apart mid-break */
  tr {
    break-inside: avoid;
  }

  /* Protect headings and footers as well */
  h1, h2, h3, .invoice-header {
    break-after: avoid;
  }
}

3. Repeating table headers across page breaks with thead

A properly marked-up <thead> is automatically repeated by every mainstream browser on each new page a table continues onto, provided the table actually uses thead and tbody rather than plain tr elements sitting directly under table. This automatic repetition is not strictly a CSS property but built-in behavior of HTML table semantics combined with the browser's print model.

The most common reason this behavior fails in practice is missing or incorrectly structured markup: if the header row is only visually highlighted with font-weight: bold but technically marked up as a plain tr inside tbody, the browser does not recognize it as a repeatable header and only shows the column labels on the first page.


<table class="order-items">
  <thead>
    <tr>
      <th>Item</th>
      <th>Qty</th>
      <th>Unit price</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>Product A</td><td>2</td><td>19.90 EUR</td><td>39.80 EUR</td></tr>
    <tr><td>Product B</td><td>1</td><td>49.00 EUR</td><td>49.00 EUR</td></tr>
    <!-- ... many more rows ... -->
  </tbody>
</table>

4. tfoot for totals rows: fixed to page end or only table end

Besides thead, browsers also support <tfoot> as a repeatable footer area, but with one important difference: unlike the table header, the footer by default only appears at the actual end of the entire table, not on every intermediate page. For a running subtotal that is meant to appear at the bottom of every page, tfoot alone is not enough.

For that pattern, common on multi-page invoices with a running subtotal, you need either a combination of several deliberately repeated rows within the content area itself, or a server-side split of the table into several smaller tables each with its own subtotal, because plain CSS has no mechanism for page-relative running totals.

5. Working together with @page: header and footer of the printed page

While thead repeats the table header within the content flow, @page rules with their margin boxes control the actual header and footer of the printed page itself, such as a company logo, page number, or invoice number meant to appear on every page independently of the table content. The two mechanisms complement each other: thead for the domain-specific table header, @page margin boxes for the document-wide page frame.

An order confirmation with many line items typically benefits from both at once: the margin box at the top of the page shows the company logo and order number on every page, while the repeated thead directly above the table supplies the column labels for the continued item list. That keeps every printed page readable without losing context, even when viewed in isolation.


@page {
  size: A4;
  margin: 2cm 1.5cm;

  @top-right {
    content: "Order confirmation " counter(page) " / " counter(pages);
  }
}

6. Common print errors on order confirmations and invoices

By far the most common mistake is a table whose header row is technically a plain tr inside tbody, so the column labels only show up on page one and every following page sits without context. The second most common mistake is a missing break-inside: avoid, which lets individual line item rows get sliced right through quantity and price, which on an invoice quickly looks like a calculation error rather than a layout bug.

A third, subtler mistake comes from fixed pixel heights on table cells combined with variable font size in print: if the actual cell content does not fit into the fixed height, text can get clipped without the browser producing any error message. Fixed heights on table cells should generally be avoided in print stylesheets; the content should determine the row height, not the other way around.

7. Testing page breaks without an actual printer

The browser's print preview, usually reachable via Ctrl/Cmd + P, shows page breaks almost identically to the eventual PDF or paper printout, making it the fastest testing tool in everyday development. For automated regression tests, the same effect can be produced with headless browsers that export a page directly as a PDF, providing a reproducible basis for visual comparison tests.

It is especially important to test different amounts of data, not just a single sample order: a table with exactly one row, one with just enough rows that the break falls right before or right after the last row of a page, and a very long list spanning several pages. It is precisely these edge cases where most bugs in the break logic show up.

8. Limits of plain CSS versus server-side PDF export

Plain browser CSS reliably handles page breaks, repeated table headers, and simple margin boxes, but reaches its limits with more complex layout requirements such as precisely controlled running subtotals per page or pixel-perfect positioning for pre-printed forms. Such requirements can sometimes be worked around with CSS, but the result is often more fragile than a dedicated server-side PDF library.

For regulatorily sensitive documents like invoices with legally mandated formatting, a deliberate decision pays off: simple, frequently changed print views like order confirmations remain well maintainable with CSS, while highly formalized, less frequently changed documents like tax invoices can benefit from server-side PDF generation with exact layout control.

9. A checklist for multi-page tables in print

Before shipping an invoice or order confirmation layout, a short check pays off: is the header row correctly marked up in thead, is break-inside: avoid set on tr, have fixed pixel heights on cells been removed, and has the print preview been tested with both a very long and a very short sample list.

These four points cover the vast majority of problems that come up in practice with multi-page tables in print, and every one of them can be solved directly in CSS without JavaScript or extra server-side tooling.

Rule Effect Repeats per page Typical use
tr { break-inside: avoid } Row stays intact or moves entirely to the next page Re-evaluated per row Order line items, invoice rows
<thead> Column header is repeated automatically Yes, on every following page Table headers on long lists
<tfoot> Footer appears only at the table's actual end by default No, only once Grand total at the end of the table
@page { @top-right } Page-margin content like page number or logo Yes, on every page Document-wide headers and footers
Fixed cell height (px) Can clip content, no error shown Affects every row Should be avoided in print

Mironsoft

Modern CSS, layout architecture and rendering performance

CSS that stays maintainable instead of breaking with every change?

We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.

CSS Audit

Systematically uncovering specificity issues, cascade conflicts and unused selectors.

Architecture Refactoring

Introducing cascade layers, custom properties and design tokens cleanly.

Performance Tuning

Fixing layout thrashing, expensive selectors and rendering bottlenecks.

10. Summary

Print Tables and Page Breaks: The Essentials at a Glance

break-inside: avoid

Set on tr, it stops a row from being torn apart mid-break; the row instead moves to the next page as a whole.

thead as repeat header

A properly marked-up thead is automatically repeated on every following page by browsers, while tfoot only appears at the table's end.

Most common bug

Header rows that are technically a plain tr in tbody only show up on page one, leaving following pages without context.

Limits of CSS

Running per-page subtotals and pixel-perfect form positioning often require server-side PDF generation instead.

11. FAQ: Print Tables and Page Breaks: The Essentials at a Glance

1How do I stop a table row from being torn apart across a page break?
With break-inside: avoid on the tr element. The browser then moves the entire row to the next page instead of slicing it in the middle.
2Does the table header repeat automatically on every printed page?
Yes, if it is correctly marked up as thead. If the header row is instead a plain tr inside tbody, it only appears on the first page.
3Why is the automatic repetition not working for me?
Usually because the header row is only visually bolded but not technically marked up as thead. The browser then does not recognize it as a repeatable area.
4Does tfoot also appear on every page like thead?
No. tfoot by default only appears at the actual end of the entire table, not on every intermediate page. tfoot alone is not enough for a running subtotal per page.
5How do I combine thead with document-wide headers and footers?
thead repeats the domain-specific table header within the content, while @page margin boxes like @top-right independently show the page number or logo on every page. The two mechanisms complement each other.
6What is the most common mistake on multi-page printed invoices?
A header row that is not technically declared as thead, leaving following pages without column labels. A missing break-inside: avoid, which slices rows apart, is the second most common mistake.
7Should table cells get fixed pixel heights in print?
No. Fixed heights can clip content if the actual text does not fit, without any error message appearing. Row height should be driven by content, not the other way around.
8How do I test page breaks without an actual printer?
With the browser's print preview via Ctrl or Cmd plus P. For automated tests, headless browsers that export a page directly as a PDF work well.
9Can CSS show a running subtotal on every printed page?
Not reliably with plain CSS alone. That requires either a server-side split of the table into several sub-tables or a dedicated PDF generation tool.
10When is server-side PDF rendering worth it over plain print CSS?
For regulatorily formalized documents like tax invoices with exact formatting requirements. Simple, frequently changed views like order confirmations remain well maintainable with CSS.