@satzstrom/primitives

Primitives

React components for pages, flowing content, and document structure. Use ordinary HTML and CSS inside them.

Installation
npm install @satzstrom/primitives

Document

Root of a Satzstrom document. Contains fixed Page components, flowing PageMaster components, or both. Defines language, reading direction and PDF metadata.

Signature
function Document(props: DocumentProps): ReactNode
Document Props
NameTypeDescription
childrenReactNodeDirect children must resolve to Page or PageMaster components.
title?stringPDF title; required for PDF/UA-1.
author?stringPDF author.
subject?stringPDF subject.
keywords?string[]Keywords for PDF metadata.
lang?stringDocument language; de by default.
dir?"ltr" | "rtl"Reading direction; ltr by default.
bookmarks?SequenceDefinition<boolean>Sequence used to create PDF bookmarks.
labels?DocumentLabelsOverrides localized accessible labels.
className?stringCSS class of the document root element.
Example
import { Document, Page } from "@satzstrom/primitives";export default function Invoice() {  return (    <Document title="Invoice 2026-0718" author="Brückner Studio" lang="en">      <Page size="A4">…</Page>    </Document>  );}

Page

Creates exactly one freely composed physical page. Children can be static or receive page values through a render function. For custom sizes, landscape puts the longer dimension horizontally and portrait vertically; omitted orientation preserves width and height.

Signature
function Page(props: PageProps): ReactNode
Page Props
NameTypeDescription
childrenReactNode | ((PageContext) => ReactNode)Page content or a render function with page and pages.
size?PageSizeName | { width; height }Paper size; A4 by default. Custom dimensions use millimeters.
orientation?"portrait" | "landscape"Page orientation.
bleed?numberNon-negative bleed in millimeters.
cropMarks?booleanEnables crop marks.
className?stringCSS class of the physical page.
Example
<Page size="A4" orientation="landscape" bleed={3} cropMarks>  {({ page, pages }) => <footer>{page} / {pages}</footer>}</Page>

PageMaster

Flows its children through as many pages as needed. A custom layout places Flow between repeating regions, such as headers and footers. Without a layout, content uses the whole page.

Signature
function PageMaster(props: PageMasterProps): ReactNode
PageMaster Props
NameTypeDescription
childrenReactNodeFlowing content to paginate.
layout?ComponentType<PageLayoutProps>Page frame with page, pages, and exactly one Flow.
size?PageSettings["size"]Paper size for every page in this master.
orientation?PageSettings["orientation"]Shared page orientation.
bleed?numberBleed in millimeters.
cropMarks?booleanEnables crop marks.
className?stringCSS class of the generated pages.
Example
import { Document, Flow, PageMaster, type PageLayoutProps } from "@satzstrom/primitives";function ReportPage({ page, pages }: PageLayoutProps) {  return (    <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>      <header>Annual report</header>      <Flow style={{ flex: 1 }} />      <footer>{page} / {pages}</footer>    </div>  );}export default function Report() {  return (    <Document title="Annual report">      <PageMaster layout={ReportPage} size="A4">        <h1>Introduction</h1>        <p>This content flows across all required pages.</p>      </PageMaster>    </Document>  );}

Flow

Marks the area where PageMaster content continues. Keep Flow empty and give it space through CSS, for example with flex: 1. Standard div attributes are supported.

Signature
function Flow(props: FlowProps): ReactNode
Example
<Flow style={{ flex: 1 }} />

PageBreak

Forces a new page in the flowing content of a PageMaster.

Signature
function PageBreak(): ReactNode
Example
<PageMaster layout={ReportPage}>  <ChapterOne />  <PageBreak />  <ChapterTwo /></PageMaster>

RepeatBox

Marks a box so its border, background, and padding repeat on every fragment.

Signature
function RepeatBox<E extends ElementType = "div">(props: RepeatBoxProps<E>): ReactNode
RepeatBox Props
NameTypeDescription
as?ElementTypeRendered HTML element; div by default.
…propsComponentPropsWithoutRef<E>All props of the selected element.
Example
<RepeatBox as="aside" className="notice">  A long note that may continue across a page boundary.</RepeatBox>

defineSequence

Defines an immutable, independent numbering system for chapters, figures, or tables.

Signature
function defineSequence(config: {  name: string;  titleRequired?: boolean;}): SequenceDefinition
Example
const chapters = defineSequence({  name: "chapters",  titleRequired: true,});<Sequence sequence={chapters} title="Results">  {({ number, title }) => <h2>{number} {title}</h2>}</Sequence>

Sequence

Registers an entry in a Sequence and provides its computed number, title, and nesting depth.

Signature
function Sequence(props: SequenceProps): ReactNode
Sequence Props
NameTypeDescription
sequenceSequenceDefinitionDefinition created with defineSequence.
id?stringExplicit HTML target for references.
title?stringEntry title; required when titleRequired is set.
numberLabel?stringAccessible label for the rendered number.
children(value) => ReactNodeRender function with number, title, and depth.

Contents

Creates a table of contents with final numbers and pages from a Sequence, or passes entries to a custom component.

Signature
function Contents(props: ContentsProps): ReactNode
Contents Props
NameTypeDescription
sequenceSequenceDefinitionSource of the table-of-contents entries.
className?stringClass of the default table of contents.
asChild?truePasses entries to exactly one custom React element.
Example
import { Contents, type SequenceEntry } from "@satzstrom/primitives";function CustomContents({ entries = [] }: { entries?: SequenceEntry[] }) {  return <ol>{entries.map(entry => (    <li key={entry.id} style={{ marginLeft: entry.depth * 12 }}>      <a href={"#" + entry.id}>{entry.number} {entry.title}</a> — {entry.page}    </li>  ))}</ol>;}// SequenceEntry = { id: string; number: string; title?: string;//   page: number; depth: number; parentId?: string }<Contents sequence={chapters} asChild><CustomContents /></Contents>

Ref

Links to an HTML ID and displays its computed number, title, or final page.

Signature
function Ref(props: RefProps): ReactNode
Ref Props
NameTypeDescription
targetstringHTML ID of the referenced target.
value?"number" | "title" | "page"Output value; number by default.
className?stringCSS class of the link.
Example
See <Ref target="results" value="number" /> on page <Ref target="results" value="page" />.

Footnote

Places a numbered call in the text. Its content appears in the authored Footnotes region, with shared Markdown/JSX numbering and backlinks.

Signature
function Footnote(props: { children: ReactNode; label?: string }): ReactNode
Example
This statement needs a source<Footnote>Source: Annual report 2025.</Footnote>.

Footnotes

An empty placeholder in a PageMaster layout, outside Flow, or inside a fixed Page. Style and limit its height with CSS. It is hidden when empty and adds no separator. Long notes continue in the same PageMaster; a fixed Page reports overflow.

Signature
function Footnotes(props: FootnotesProps): ReactNode

Existing documents with footnotes need one empty Footnotes component outside Flow in each PageMaster layout that uses notes. Copy the complete document.tsx example to get started. For a fixed Page, place Footnotes inside that Page.

The full-height frame gives the percentage limit a definite height. Footnotes can use up to 20% here; Flow receives the remaining space. Change the limit, font and spacing to suit your layout.

Example
import { Document, PageMaster, Flow, Footnote, Footnotes } from "@satzstrom/primitives";function PageLayout() {  return (    <div style={{ height: "100%", padding: "15mm", display: "flex", flexDirection: "column" }}>      <Flow style={{ flex: 1, minHeight: 0 }} />      <Footnotes style={{ maxHeight: "20%", flexShrink: 0, fontSize: "10pt" }} />    </div>  );}export default function Report() {  return (    <Document title="Report with footnotes" lang="en">      <PageMaster size="A4" layout={PageLayout}>        <h1>A report with footnotes</h1>        <p>A statement with a source.<Footnote>Source: Annual report 2025.</Footnote></p>      </PageMaster>    </Document>  );}

Math

Renders KaTeX as visible HTML plus accessible MathML, inline or as a separate display equation.

Signature
function Math(props: {  children: string;  display?: boolean;  label?: string;}): ReactNode
Example
<Math>{String.raw`E = mc^2`}</Math><Math display label="Quadratic formula">  {String.raw`x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}`}</Math>

Markdown

Renders GitHub-Flavored Markdown with tables, footnotes, and mathematics. Raw HTML is deliberately skipped. Use single dollars for inline math, double dollars for display math, and escape literal dollar signs.

Signature
function Markdown(props: {  children: string;  labels?: MarkdownLabels;}): ReactNode
Example
<Markdown>{`## Results| Quarter | Revenue || --- | ---: || Q1 | 42.000 € |A reference with a footnote.[^1][^1]: Internal analysis.`}</Markdown>

useRenderReady

Delays measurement and rendering while asynchronous browser content is not ready.

Signature
function useRenderReady(ready: boolean): void
Example
function RemoteChart() {  const { data, isLoading } = useReportData();  useRenderReady(!isLoading);  return data ? <Chart data={data} /> : null;}