Skip to content

Core concepts

The four inputs

Every BroadPaper render is a pure function of four things:

InputWho provides itWhat it is
TemplateYour users (via the designer)JSON describing pages, sections, rows, columns and blocks, with bindings written as expressions.
Data sourcesYour developersSchemas describing the shape of data: client.firstName is a string, portfolio.holdings is a list of objects…
DataYour application at runtimeA plain JSON object keyed by data source id.
ThemeYour developers / your customersColours, fonts, logo and brand details. Templates reference theme tokens, never literal brand colours.

render(template, data, theme) is deterministic: the same inputs give the same pages, and the same PDF bytes as long as the clock, the fonts, the external assets and the renderer version are also the same. What each one guarantees spells that out.

The document model

Template
├── page            size, orientation, margins, header/footer heights
├── styles          named text styles (h1…small), table, card, spacing
├── header          sections shown on every page (optionally different on page 1)
├── footer          sections shown on every page
├── body            sections
│   └── Section
│       ├── Row → Column → Blocks…
│       └── Blocks (heading, text, table, repeater, chart, custom…)
└── library         reusable sections saved with the template

Every node is { id, type, props, style?, children?, visibleWhen?, flow? }. Blocks are looked up in the block registry, which is how custom blocks become first-class citizens.

Design mode and preview mode

The designer has two modes that use the same rendering pipeline:

  • Design shows tokens as chips (Client › First name) so it is obvious what is dynamic, shows one representative repeater iteration, a few table rows, and keeps conditionally hidden content visible but dimmed.
  • Preview evaluates everything with a chosen sample data set. What you see is exactly the PDF.

Pagination

BroadPaper does not let the browser paginate. Each section is measured once as a continuous "galley"; a pure paginator then assigns clip windows to fixed-size pages honouring:

  • keepTogether (cards, KPIs, repeater items by default)
  • keepWithNext (headings by default)
  • orphan/widow minimums for text lines and table rows
  • repeated table headers on continuation pages
  • explicit page breaks and breakBefore/breakAfter
  • multi-column rows, cut at a y that is valid in every column

Anything taller than a page that cannot be split is cut at page boundaries with a warning rather than dropped. See docs/ARCHITECTURE.md in the repository for the full reasoning.

Style cascade

Theme defaults → Document text styles (h1, body, …) → Block defaults → Instance overrides

Changing the document's H2 style restyles every H2. Instance overrides can be disabled by the host (features.styleOverrides = false) so templates stay on-brand.