PagesLast updated on
Last updated on
Measures the document once and lays it out as paginated sheets.
Pages is the one component that actually paginates: it renders the tree
it's given once, off-screen, to measure it, then renders that same tree
again, once per page, showing on each only the keeps the plan assigned to
it. Everything underneath — every Field, Section, and Table — is
unaware it might be rendering more than once.
Preview
Installation
npx shadcn@4 add @paradoc/pagesOr, with the Paradoc CLI, which writes the namespace into components.json for you:
npx paradoc-cli add pagesInstalls to components/paradoc/pages.tsx and brings along paper.
Usage
import { Pages } from "@/components/paradoc/pages";<Pages className="w-full overflow-hidden bg-neutral-100 p-10"> <Document artifact={proposalForm} data={shortProposalData}> <Field path="provider" /> </Document></Pages>| Prop | Type | Default | Description |
|---|---|---|---|
| className? | string | — | Classes for the outer frame that scales and scrolls the paginated stack. |
| onPaginate? | (plan: PagePlan) => void | — | Called with the measured page plan whenever pagination changes. |
| furniture? | PageFurniture | — | What every sheet carries outside the flow: a header, a footer, a stamp. Drawn inside the document's margin, so the page count does not change. |
| children | ReactNode | — | The document tree measured once and paginated into sheets. |
Composition
Pages wraps a Document, or a
Bundle of several, and draws each page it produces
on its own Paper sheet, sized to the document's own
page geometry tokens. Every Keep Together
beneath it — a Field, a titled Section's heading, each of a Table's
rows — is what the plan actually places page by page; Pages moves nothing
itself, it only decides which keeps belong on which page.
onPaginate is called with the measured plan whenever pagination changes,
which is how a caller — the docs preview included — reads the page count or
drives a PDF render from the same plan the browser laid out. A Table whose
rows continue past a page break gets its header copied onto the continuing
page from that same plan; the copy carries data-keep-repeat so it's never
confused with the header's one real place in the flow.
Unlike Paper alone, which draws one sheet and lets
content run past its bottom, Pages is what keeps a document's rendered
pages matching what a reader would expect from a printed or exported copy.
Page furniture
furniture is what every sheet carries outside the flow: a header band, a
footer band, and a stamp drawn across the whole sheet behind the content.
Each slot is composed content, and
PageNumber is the one that prints the sheet it is
on. The PDF render from @paradoc/react-pdf takes the same
object.
import { renderPdf } from "@paradoc/react-pdf";
import { PageNumber } from "@/components/paradoc/page-number";
import { Pages } from "@/components/paradoc/pages";
const furniture = {
header: <span>Northwind Partners LLP</span>,
footer: <PageNumber />,
stamp: <span className="text-6xl text-neutral-200">DRAFT</span>,
};
<Pages furniture={furniture}>{document}</Pages>;
// and, on the server, the same object:
await renderPdf(document, { plan, furniture });A band is drawn inside the margin the document already declares, between the
paper's edge and the content box, so the content budget, the page plan and the
page count are exactly what they are without it: adding a footer never
repaginates a document. The price is a height limit. A band taller than the
margin leaves it fails by name, in the preview and in the PDF, saying which slot, how tall it laid
out, and how much margin it had; widen the document's marginPx token or
shorten the band.
The stamp is drawn across the whole sheet, centred on it and behind the
content, so it takes no room in the margin. Its limit is the sheet itself: a
stamp that lays out taller than the paper, or with a word or a
whitespace-nowrap line wider than it, fails by name rather than being cut off
at the paper's edge on every page. It is measured before any rotation.
Furniture is drawn around the document rather than inside it, so it cannot read a field path. Pass what it prints in as props.
Each engine declares which slots it draws, and both engines draw all three. The experimental Chromium adapter prints the header and the footer through its own print templates and repeats the stamp on every page; it cannot print a page number in the stamp, and refuses one by name. An engine that does not draw a slot fails naming the engine and the slot rather than writing a document whose every page is missing its header.
Variants
Pages takes an optional className, onPaginate, and furniture.
Multi-page overflow
A longer proposal's line items run past three pages, so Pages lays the
same one tree out across several sheets and repeats the table's header on
every page it continues onto.
Page furniture
furniture gives every sheet a running head, a numbered foot, and a stamp
behind the content, all drawn inside the margin, so the page count is the one
the same document has without them.
Draft watermark
A stamp of large, light text turned with -rotate-45 says DRAFT across
every sheet, rising from the bottom left. Rotation is in the verified class
vocabulary, so both PDF engines draw the stamp turned and centred as the
preview does.
Custom frame
A custom className replaces the default scroll frame's background and
padding.
Page count
onPaginate reports the measured plan; here it's used to show the resulting
page count beside the preview.