Skip to content
Inkoxide

Search covers every page and heading of the docs. Use ↑ ↓ and ↵.

Pages, headers and footers

Edit this page

<Page size="A4" className="p-16 text-xs">
  <Header className="flex-row justify-between text-gray-500">
    <Text>ACME Corp.</Text>
    <Text pages="notFirst">Annual report 2026</Text>
  </Header>
  <Footer className="text-gray-400">
    <Text className="text-center">Page <PageNumber /> of <TotalPages /></Text>
  </Footer>

  <Text className="text-xl font-bold" break={{ minPresenceAhead: 80 }}>Results</Text>
  <View break="avoid">{/* a signature block that stays on one page */}</View>
  <PageBreak />
</Page>
  • Header and footer. They sit in the page margin, line up with the content and are centred vertically. Their own vertical padding or alignment (items-* in a flex-row, justify-* in a column) overrides that.

  • Showing on some pages. pages="first" | "notFirst" | "last" | "notLast" | "odd" | "even", or a list of page numbers, shows an element in a header, a footer or a fixed element only on those pages. Page numbers count from 1 across the whole document; a negative number counts from the end, so pages={[1, -1]} is the first and the last page. 0 and an empty list are errors.

  • In the page body. pages works on any element of the body too. The element keeps its place in the layout on every page and is painted only on those pages: on others it leaves an empty space, and of a box split across pages only the parts on those pages are drawn.

  • Paragraphs. orphans and widows keep at least that many lines together at the foot and head of a page.

  • Page breaks. <PageBreak />, break="page-before" and break="page-after" start a new page at any depth of the body, when content follows them: a break at the end adds no blank page, and neither does one with nothing above it on its page. A break before the first child of a View takes the View along. They are errors inside a flex-row, grid, wrapping or reversed View and inside an absolutely positioned, rotated or transformed element, where they would do nothing. On an element, break="page-before" and break="page-after" are errors only when it is absolutely positioned (it is out of the flow); on a rotated or transformed element they work.

  • Keeping together. break="avoid" (wrap={false}) moves an element to the next page whole when it does not fit; a PageBreak inside it still breaks, and the part before it moves whole. A flex-row, grid or wrapping View moves whole too. Taller than a page, both split: a row between the lines of its children, each column continuing on the next page.

  • Content that depends on the page. Use render={({ pageNumber, totalPages }) => …} on a Text or View in a header, a footer or a fixed element. A component rendered by a render prop can read the same numbers with the usePage() hook, which throws anywhere else:

    function Folio() {
      const { pageNumber, totalPages } = usePage();
      return <Text>{pageNumber === totalPages ? 'Last page' : `Page ${pageNumber}`}</Text>;
    }
    
    <Footer>
      <View render={() => <Folio />} />
    </Footer>

On every page: fixed

Use Header and Footer for page chrome: titles and page numbers in the margins, with room kept for them. For a watermark, a page border or a stamp, mark an element fixed. It is painted on every page of its Page, over the content or, with a negative zIndex, under it:

<Page size="A4" style={{ padding: 48 }}>
  <Text
    fixed
    style={{ position: 'absolute', top: '45%', left: 0, right: 0, textAlign: 'center' }}
    className="rotate-[-30deg] text-[64pt] font-bold text-red-500/15"
  >
    DRAFT
  </Text>
  <View
    fixed
    style={{ position: 'absolute', top: -16, right: -16, bottom: -16, left: -16 }}
    className="border border-gray-300"
  />
  <Text
    fixed
    pages="notFirst"
    style={{ position: 'absolute', bottom: -36, left: 0, right: 0, textAlign: 'center' }}
    render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`}
  />
  {/* the content */}
</Page>
  • Placement. A fixed element goes directly in the Page, with position: 'absolute' (or className="absolute"). Its top, right, bottom, left and percentages are measured from the page's content box, the area inside the margin, on every page. Negative values reach into the margin: with padding: 48, bottom: -36 is 12pt above the edge of the paper. On a one-page document, adding fixed moves nothing.
  • Stacking. A fixed element is painted over the content, and under the Header and Footer. With a negative zIndex (-z-10) it is painted under the content instead, over the page background: a watermark that text and filled boxes cover. Fixed elements stack among themselves by zIndex, then in the order they are written.
  • Space. It takes none: the content flows under it. Content that needs room on every page belongs in a Header or Footer, with a margin sized for it.
  • Page numbers. render, <PageNumber /> and <TotalPages /> show each page its own numbers, and pages chooses the pages it appears on.
  • Links, ids and bookmarks. An href makes a link on every page. An id (the target of href="#id") and a bookmark point to the first page.
  • Not allowed. fixed on an element inside another element, in a Table, Header or Footer, on a Header or Footer itself, on a Text inside a Text, on a form field, or without position: 'absolute'. A fixed element is painted whole on every page, so break, wrap={false} and minPresenceAhead on it or inside it, and a PageBreak inside it, would do nothing (as in a Header or Footer). Each is an error that names the component.

Page sizes

sizeSizes
4A0, 2A0, A0–A10ISO 216 A series (A4 is 210 × 297 mm)
B0–B10ISO 216 B series
C0–C10ISO 269 C series (envelopes)
RA0–RA4, SRA0–SRA4ISO 217 untrimmed paper
Letter, Legal, Tabloid, Executive, Folio8.5 × 11, 8.5 × 14, 11 × 17, 7.25 × 10.5 and 8.5 × 13 in
ID1ID-1 card (a bank card), 53.98 × 85.6 mm

Presets are portrait, and their names match in any case ("letter", "LETTER"). Add -landscape ("A5-landscape") or orientation="landscape" to turn one sideways. Any other size is [width, height] or { width, height }, and [width] or a single length makes a square page. Lengths are points or strings with a unit:

<Page size="a4-landscape" />
<Page size={[400, 600]} />
<Page size={['100mm', '150mm']} />
<Page size="80mm" />

PAGE_SIZES has every preset in points.

Synced from packages/render/GUIDE.md in the inkoxide repository.