---
title: "Headers, footers and page breaks"
url: https://recursiveai.org/onerender/cookbook/paged-media/
description: "Control a PDF’s pages from CSS with OneRender: page size and margins with @page, a running header and “Page 1 of 5” footer in the margin boxes, forced…"
version: "1.0.0"
---

# Headers, footers and page breaks

Control a PDF’s pages from CSS with OneRender: page size and margins with `@page`, a running header and “Page 1 of 5” footer in the margin boxes, forced page breaks, and a landscape page for one section.

```csharp
using System.Text;
using OneRender.Pdf;

var rows = new StringBuilder();
for (int i = 1; i <= 60; i++)
    rows.Append($"<tr><td>{i:000}</td><td>Order {1000 + i}</td><td>${i * 12.5m:0.00}</td></tr>");

string html = $$"""
<!DOCTYPE html>
<html lang="en">
<head>
<title>Quarterly report</title>
<style>
  @page {
    size: A4;
    margin: 25mm 20mm;
    @top-left { content: string(chapter); font: 9pt sans-serif; color: #666 }
    @bottom-right { content: "Page " counter(page) " of " counter(pages); font: 9pt sans-serif }
  }
  @page :first { @top-left { content: none } }
  @page wide { size: A4 landscape }
  body { font: 11pt/1.5 serif }
  h1 { string-set: chapter content() }
  .chapter { break-before: page }
  .wide { page: wide }
  table { width: 100%; border-collapse: collapse }
  th { background: #eee; text-align: left }
  td { padding: 4px 8px; border-bottom: 1px solid #ccc }
</style>
</head>
<body>
  <h1>Summary</h1>
  <p>The orders table starts on the next page.</p>
  <section class="chapter">
    <h1>Orders</h1>
    <table>
      <thead><tr><th>#</th><th>Order</th><th>Total</th></tr></thead>
      <tbody>{{rows}}</tbody>
    </table>
  </section>
  <section class="chapter wide">
    <h1>Appendix</h1>
    <p>This section is on a landscape page.</p>
  </section>
</body>
</html>
""";

HtmlPdfResult result = HtmlPdf.ToPdf(html, File.Create("report.pdf"));
Console.WriteLine($"{result.PageCount} pages");
```

```text
5 pages
```

Page 1 is the summary, with no header. The table starts on page 2 and continues on pages 3 and 4, with “Orders” in the top-left corner of each. Page 5 is the appendix, on a landscape A4 page. Every page has “Page n of 5” at the bottom right.

## What each rule does

- **`@page { size; margin }`** sets the page box. Sizes take keywords (`A3`, `A4`, `A5`, `B4`, `B5`, `letter`, `legal`, `ledger`), `landscape` or `portrait`, or one or two lengths. The page setup in `HtmlPdfOptions` is only the default for a document without `@page`.
- **Margin boxes** (`@top-left`, `@top-center`, `@bottom-right` and the other 13) hold generated content: text, `counter(page)`, `counter(pages)` and `string()`. They are drawn as artifacts, so the running header and page numbers stay out of the PDF’s structure tree and screen readers don’t read them on every page.
- **`string-set`** copies an element’s text into a named string, and `string(chapter)` shows the current value on each page. `:first`, `:left`, `:right` and `:blank` select pages.
- **`break-before: page`** (also `break-after`, and the older `page-break-before`) forces a new page. `break-inside: avoid` keeps a block together. `orphans` and `widows` apply to paragraphs.
- **Named pages.** `page: wide` puts an element on pages of the `@page wide` rule, starting a new page when the name changes.

## Tables across pages

A table breaks between rows, and a row is never split. In 1.0.0 a table that continues onto another page keeps room for its `thead` and `tfoot` at the top and bottom of that page, but the copies don’t show, so here page 3 starts with an empty band and then row 028. If each page of a long table needs its headings, split it into one table per page yourself.

## Links and tables of contents

Links to `#id` targets become links inside the PDF. A contents list can show page numbers with `target-counter(attr(href), page)` and dot leaders with `leader(dotted)`:

```css
.toc a::after { content: leader(dotted) target-counter(attr(href), page) }
```

## Good to know

- Footnotes, `position: running()` and vertical writing modes are not implemented.
- Units in `@page` are CSS units; `PdfPageSetup` in the options takes CSS pixels (96 per inch).
- A document that would need more than `MaxPages` pages throws `PdfRenderException`.

The paged-media rules are in [Architecture](https://recursiveai.org/onerender/docs/architecture/#paged-media-paged-layoutpagefragmentationcs-treecontentlistcs-treegeneratedcontentcs) and [PDF output](https://recursiveai.org/onerender/docs/pdf/#pages).
