Pages

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

Rendering live preview requires JavaScript.

Installation

npx shadcn@4 add @paradoc/pages

Or, with the Paradoc CLI, which writes the namespace into components.json for you:

npx paradoc-cli add pages

Installs 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>
PropTypeDefaultDescription
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.
childrenReactNode—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.

Rendering live preview requires JavaScript.

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.

Rendering live preview requires JavaScript.

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.

Rendering live preview requires JavaScript.

Custom frame

A custom className replaces the default scroll frame's background and padding.

Rendering live preview requires JavaScript.

Page count

onPaginate reports the measured plan; here it's used to show the resulting page count beside the preview.

Rendering live preview requires JavaScript.

On this page