Pages, headers and footers
<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 aflex-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, sopages={[1, -1]}is the first and the last page.0and an empty list are errors. -
In the page body.
pagesworks 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.
orphansandwidowskeep at least that many lines together at the foot and head of a page. -
Page breaks.
<PageBreak />,break="page-before"andbreak="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 aViewtakes theViewalong. They are errors inside aflex-row, grid, wrapping or reversedViewand inside an absolutely positioned, rotated or transformed element, where they would do nothing. On an element,break="page-before"andbreak="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; aPageBreakinside it still breaks, and the part before it moves whole. Aflex-row, grid or wrappingViewmoves 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 aTextorViewin a header, a footer or a fixed element. A component rendered by arenderprop can read the same numbers with theusePage()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, withposition: 'absolute'(orclassName="absolute"). Itstop,right,bottom,leftand percentages are measured from the page's content box, the area inside the margin, on every page. Negative values reach into the margin: withpadding: 48,bottom: -36is 12pt above the edge of the paper. On a one-page document, addingfixedmoves nothing. - Stacking. A fixed element is painted over the content, and under the
HeaderandFooter. With a negativezIndex(-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 byzIndex, 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
HeaderorFooter, with a margin sized for it. - Page numbers.
render,<PageNumber />and<TotalPages />show each page its own numbers, andpageschooses the pages it appears on. - Links, ids and bookmarks. An
hrefmakes a link on every page. Anid(the target ofhref="#id") and abookmarkpoint to the first page. - Not allowed.
fixedon an element inside another element, in aTable,HeaderorFooter, on aHeaderorFooteritself, on aTextinside aText, on a form field, or withoutposition: 'absolute'. A fixed element is painted whole on every page, sobreak,wrap={false}andminPresenceAheadon it or inside it, and aPageBreakinside it, would do nothing (as in aHeaderorFooter). Each is an error that names the component.
Page sizes
size | Sizes |
|---|---|
4A0, 2A0, A0–A10 | ISO 216 A series (A4 is 210 × 297 mm) |
B0–B10 | ISO 216 B series |
C0–C10 | ISO 269 C series (envelopes) |
RA0–RA4, SRA0–SRA4 | ISO 217 untrimmed paper |
Letter, Legal, Tabloid, Executive, Folio | 8.5 × 11, 8.5 × 14, 11 × 17, 7.25 × 10.5 and 8.5 × 13 in |
ID1 | ID-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.