# Headers and footers

> Add running headers and footers with page numbers, dates, logos and data, in simple text mode or with your own HTML.



Headers and footers repeat on every page of a PDF. They are configured in `output.pdf.header` and `output.pdf.footer` (or stored as defaults on the template), and they come in two flavours: a simple three-slot text mode, and full HTML.

| Option                    | Type    | Default  | Description                                      |
| ------------------------- | ------- | -------- | ------------------------------------------------ |
| `enabled`                 | boolean | `false`  | Draw this header or footer                       |
| `html`                    | string  | none     | HTML mode: the markup to render on every page    |
| `left`, `center`, `right` | string  | none     | Simple mode: text for the three slots            |
| `font_size`               | string  | `"9px"`  | Base font size for the header or footer document |
| `height`                  | string  | `"15mm"` | Height reserved on every page                    |

## Simple mode [#simple-mode]

Give one or more of `left`, `center` and `right` a line of text. Tokens are replaced per page, and your data is available through the template language:

```json
{
  "output": {
    "format": "pdf",
    "pdf": {
      "margin": { "top": "20mm", "right": "16mm", "bottom": "18mm", "left": "16mm" },
      "footer": {
        "enabled": true,
        "left": "{{ company.name }} · Invoice {{ invoice.number }}",
        "right": "Page {{page}} of {{pages}}",
        "font_size": "8pt",
        "height": "12mm"
      }
    }
  }
}
```

| Token       | Replaced with                      |
| ----------- | ---------------------------------- |
| `{{page}}`  | The current page number            |
| `{{pages}}` | The total number of pages          |
| `{{date}}`  | The date the document was rendered |
| `{{title}}` | The document title                 |

The `{{date}}` token uses the renderer's default date format. When the format matters, pass the date in your data and format it yourself, for example `{{ invoice.issue_date | format_date("long", "en-GB") }}`.

## HTML mode [#html-mode]

Set `html` for full control. The markup is a small standalone document that is drawn on every page; spans with particular class names are filled in by the renderer:

```json
{
  "header": {
    "enabled": true,
    "html": "<div style=\"width:100%;padding:0 16mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:center;font-size:8pt;color:#555\"><span>{{ company.name }}</span><span>Page <span class=\"page\"></span> of <span class=\"pages\"></span></span></div>",
    "height": "16mm"
  }
}
```

The same markup, formatted for reading:

```html
<div style="width:100%;padding:0 16mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:center;font-size:8pt;color:#555">
  <span>{{ company.name }}</span>
  <span>Page <span class="page"></span> of <span class="pages"></span></span>
</div>
```

| Class                        | Filled with                                              |
| ---------------------------- | -------------------------------------------------------- |
| `page` (alias `pageNumber`)  | The current page number                                  |
| `pages` (alias `totalPages`) | The total number of pages                                |
| `date`                       | The render date                                          |
| `title`                      | The document title                                       |
| `url`                        | The document URL, which is most useful for `url` renders |

Put the class on an empty element: any content inside it is replaced.

## How headers and footers are rendered [#how-headers-and-footers-are-rendered]

The header and footer are rendered in an isolated document, separate from your page. That has a few consequences worth knowing:

* **Your document's CSS does not apply.** Style the header with inline styles.
* **Network resources cannot be loaded.** Images referenced with `<img src="…">` and fonts declared with `@font-face` are downloaded and inlined as data URIs for you, up to 2 MB each. You can also inline an image yourself with [`image_data_uri()`](/docs/template-language#image_data_uri).
* **The base font size is small.** It comes from `font_size` (`9px` by default), so set it, or set sizes on the elements themselves.
* **Name a concrete font, not only a generic family.** In the header and footer document a bare `font-family: sans-serif` resolves to a serif face. Give a family the engine has, with fallbacks, for example `font-family: "Inter", "Liberation Sans", Arial, sans-serif`.
* **The area spans the full page width**, including the page margins. Add horizontal padding that matches your page margins so the header lines up with the body text.
* **Background colours print as specified**, without extra CSS on your side.
* **The template language runs** with the same data as the document, and output is autoescaped.

## Height and margins [#height-and-margins]

A header is drawn inside the top margin and a footer inside the bottom margin, so the margin has to be large enough to hold it.

* If you don't set `margin.top` or `margin.bottom`, the margin follows the `height` of the header or footer.
* If you do set them, keep the margin at least as large as `height`, plus a few millimetres of breathing room. A margin smaller than the height produces a `header_footer_overlap` warning on the render, and body content can end up underneath the header.

For the example above, a `height` of `16mm` pairs well with a `margin.top` of `22mm`.

## Template defaults [#template-defaults]

Templates store their own header and footer in the editor's **Header & footer** tab, so most requests don't need to send anything. Options you send with a render request override the template's defaults, which is useful for a "COPY" or "DRAFT" banner in one specific call.

## Examples [#examples]

A footer with page numbers and a company line:

```json
{
  "footer": {
    "enabled": true,
    "html": "<div style=\"width:100%;padding:0 18mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;font-size:7.5pt;color:#646a7c\"><span>Example GmbH · VAT DE123456789</span><span>Page <span class=\"page\"></span> of <span class=\"pages\"></span></span></div>",
    "height": "14mm"
  }
}
```

A letterhead header with a logo, using a data URI so the image is always available:

```html
<div style="width:100%;padding:0 20mm;font-family:Inter,'Liberation Sans',Arial,sans-serif;display:flex;justify-content:space-between;align-items:flex-end;font-size:8pt;color:#333">
  <img src="{{ image_data_uri(company.logo_url) }}" alt="" style="height:10mm">
  <span>{{ document.type }} {{ document.number }} · {{ document.date | format_date("medium", "en-GB") }}</span>
</div>
```

A centred confidentiality note in simple mode:

```json
{
  "header": {
    "enabled": true,
    "center": "Confidential — prepared for {{ customer.name }}",
    "font_size": "8pt",
    "height": "12mm"
  }
}
```

## Planned [#planned]

* Different headers and footers on the first page, and separate odd and even page variants
* A visual header and footer builder in the editor
* Running section titles, which repeat the current section's heading in the header, as part of the [pagination toolkit](/docs/pagination#pagination-toolkit)
