@pyreon/document-primitives — API Reference
Generated from
document-primitives'ssrc/manifest.ts— the same source that powersllms.txtand MCPget_api. Do not edit this page by hand; edit the manifest. For the conceptual guide, see document-primitives.
18 rocketstyle-based document primitives — DocDocument, DocPage, DocSection, DocRow, DocColumn, DocHeading, DocText, DocLink, DocImage, DocTable, DocList, DocListItem, DocCode, DocDivider, DocSpacer, DocButton, DocQuote, DocPageBreak. The same JSX tree renders in the browser AND exports to 14+ output formats (PDF, DOCX, XLSX, PPTX, HTML, Markdown, email, Slack, Teams, etc.). Primitives carry _documentType static markers; extractDocumentTree (from @pyreon/connector-document) walks the tree to produce a DocNode for @pyreon/document's render() to consume. DocDocument accepts reactive accessors for title / author / subject — function values are stored in _documentProps and resolved at extraction time so each export click reads the LIVE value from the underlying signal.
Features
18 primitives covering structure, text, lists, tables, code, layout
Same component tree renders in browser AND exports to 14+ formats
extractDocNode(templateFn) — one-step extraction (recommended)
createDocumentExport(templateFn) — two-step form (backward compat)
DocDocument accepts reactive accessors for title / author / subject
PR #197 fix: extractDocumentTree now calls rocketstyle components to read post-attrs metadata
Layout props in .attrs() (direction / gap), CSS in .theme()
Complete example
A full, end-to-end usage of the package:
import {
DocDocument, DocPage, DocSection, DocRow, DocColumn,
DocHeading, DocText, DocLink, DocImage, DocTable,
DocList, DocListItem, DocCode, DocDivider, DocSpacer,
DocButton, DocQuote, DocPageBreak,
extractDocNode,
} from '@pyreon/document-primitives'
import { download } from '@pyreon/document'
interface Resume { name: string; headline: string }
function ResumeTemplate(props: { resume: () => Resume }) {
return (
// title and author accept reactive accessors — extractDocNode
// resolves them at extraction time, so each export click reads
// the LIVE value from the underlying signal
<DocDocument
title={() => `${props.resume().name} — Resume`}
author={() => props.resume().name}
>
<DocPage>
<DocSection>
<DocHeading level="h1">{props.resume().name}</DocHeading>
<DocText>{props.resume().headline}</DocText>
</DocSection>
</DocPage>
</DocDocument>
)
}
// One-step extraction → render to any of 14+ formats
const tree = extractDocNode(() => <ResumeTemplate resume={store.resume} />)
await download(tree, 'resume.pdf')
await download(tree, 'resume.docx')
await download(tree, 'resume.html')
await download(tree, 'resume.md')Exports
| Symbol | Kind | Summary |
|---|---|---|
extractDocNode | function | 18 primitives: DocDocument, DocPage, DocSection, DocRow, DocColumn, DocHeading, DocText, DocLink, `DocIm |
createDocumentExport | function | Wrapper around extractDocNode. |
DocDocument | component | Root container for a document tree — produces a _documentType: "document" node. |
DocPage | component | A page boundary inside a DocDocument. |
DocSection | component | Semantic grouping inside a page. |
DocRow | component | Horizontal layout container — children flow inline with a fixed 8px gap. |
DocColumn | component | A column inside a row layout. |
DocHeading | component | Heading text — level ("h1" through "h6") controls both visual size and the semantic level emitted to outputs (HTML |
DocText | component | Paragraph / inline text. |
DocLink | component | Hyperlink within text. |
DocImage | component | An image embedded in the document. |
DocTable | component | Tabular data. |
DocList | component | Bulleted (default) or numbered (ordered) list. |
DocListItem | component | Single item inside a DocList. |
DocCode | component | Monospace code block. |
DocDivider | component | Horizontal rule — visual section separator. |
DocSpacer | component | Vertical whitespace — adds a blank vertical gap. |
DocButton | component | Call-to-action button. |
DocQuote | component | Block quote — sets off a quoted passage with an indented left border. |
DocPageBreak | component | Explicit page boundary inside a DocPage. |
DocumentPreview | component | A paper-sized PREVIEW wrapper for a document-primitive tree — it renders the Doc* subtree as centered white pages (gray |
documentTheme | constant | The default theme object for document styling/export — a plain nested config of colors (primary / text / background / |
API
extractDocNode function
extractDocNode(templateFn: () => VNode, options?: ExtractOptions): DocNode18 primitives: DocDocument, DocPage, DocSection, DocRow, DocColumn, DocHeading, DocText, DocLink, DocImage, DocTable, DocList, DocListItem, DocCode, DocDivider, DocSpacer, DocButton, DocQuote, DocPageBreak. Same component tree renders in browser AND exports — primitives carry _documentType statics that extractDocumentTree (from @pyreon/connector-document) walks to produce a DocNode for @pyreon/document's render() to consume. DocDocument's title / author / subject accept either a string OR a () => string accessor; function values are stored in _documentProps and resolved at extraction time so reactive metadata works without const initial = get() workarounds. PR #197 also fixed a latent bug in extractDocumentTree: it now CALLS rocketstyle component functions to read post-attrs _documentProps, where before it only looked at the JSX vnode's props directly — every primitive's metadata was silently dropped during export until that fix landed.
Example
import {
DocDocument, DocPage, DocHeading, DocText,
extractDocNode,
} from '@pyreon/document-primitives'
import { download } from '@pyreon/document'
const tree = extractDocNode(() => (
<DocDocument title="Quarterly Report" author="Aisha">
<DocPage>
<DocHeading level="h1">Q4 Results</DocHeading>
<DocText>Revenue grew 23% YoY.</DocText>
</DocPage>
</DocDocument>
))
await download(tree, 'report.pdf')
await download(tree, 'report.docx')Common mistakes
Calling
props.title()at the top of a template body to "fix" reactivity — components run ONCE at mount, so this captures the initial value forever. Pass the accessor through to DocDocument as-is:<DocDocument title={() => get().name}>DocRow direction: layout props (direction, gap) go in
.attrs()not.theme(). Element accepts'inline'|'rows'|'reverseInline'|'reverseRows'—'row'is NOT validFor text children reactivity, pass a signal accessor and read inside body:
<DocText>{store.field()}</DocText>Don't declare runtime-filled fields (
tag,_documentProps) in the rocketstyle.attrs<P>()generic — they leak as required JSX propsUsing
createDocumentExport(...).getDocNode()in new code — preferextractDocNode(fn)which is one call instead of two.createDocumentExportis kept for backward compat
See also: createDocumentExport
createDocumentExport function
createDocumentExport(templateFn: () => VNode): { getDocNode(): DocNode }Wrapper around extractDocNode. The wrapper-object form is kept for callers that want to pass the helper around (e.g. to wrapper components that take a DocumentExport instance). New code should use extractDocNode(templateFn) which is one call instead of two.
Example
// Two-step form (kept for backward compat). New code should
// prefer the one-step extractDocNode helper.
import { createDocumentExport } from '@pyreon/document-primitives'
const helper = createDocumentExport(() => <Resume name="Aisha" />)
const tree = helper.getDocNode()See also: extractDocNode
DocDocument component
(props: { title?: string | (() => string); author?: string | (() => string); subject?: string | (() => string); children: VNodeChild }) => VNodeChildRoot container for a document tree — produces a _documentType: "document" node. Accepts optional metadata: title, author, subject. Each accepts either a plain string OR a () => string accessor; function values are stored in _documentProps and resolved at extraction time so each export call reads the LIVE value from any underlying signal.
Example
<DocDocument title="Quarterly Report" author="Aisha" subject="Q4 2025">
<DocPage>...</DocPage>
</DocDocument>
// Reactive metadata via accessor
<DocDocument title={() => `${user().name} — Resume`}>
<DocPage>...</DocPage>
</DocDocument>Common mistakes
Passing a CALLED accessor —
title={getTitle()}— captures the value ONCE (the rocketstyle.attrs()callback runs a single time at mount). Pass the accessor ITSELF —title={getTitle}ortitle={() => userName()}— soextractDocumentTreeresolves the LIVE value on every export.Expecting a plain string
title="Q4"to update when a signal changes — a string is STATIC (captured verbatim into_documentProps); only a() => stringaccessor is re-resolved at extraction time. Use an accessor when the value comes from a signal.Passing
title={maybeUndefined}and expecting an empty-string title — anull/undefinedvalue is OMITTED from the export metadata (the field is simply absent), never stored astitle: undefined.
See also: DocPage · extractDocNode
DocPage component
(props: { size?: string; orientation?: 'portrait' | 'landscape'; children: VNodeChild }) => VNodeChildA page boundary inside a DocDocument. Paginated outputs (PDF, DOCX) treat each DocPage as a separate page; flow outputs (HTML, Markdown) render the contents inline with no page boundary. size and orientation configure paginated formats — common values: "A4", "Letter", "Legal".
Example
<DocDocument>
<DocPage size="A4" orientation="portrait">
<DocHeading level="h1">Page 1</DocHeading>
</DocPage>
<DocPage size="A4" orientation="landscape">
<DocHeading level="h1">Page 2 — landscape</DocHeading>
</DocPage>
</DocDocument>See also: DocDocument · DocPageBreak
DocSection component
(props: { direction?: 'column' | 'row'; children: VNodeChild }) => VNodeChildSemantic grouping inside a page. Default direction is "column" (children stack vertically); "row" arranges them horizontally. Use to group related content for visual rhythm and for export targets that emit semantic section markers (HTML <section>, DOCX section breaks).
Example
<DocPage>
<DocSection direction="column">
<DocHeading level="h2">Introduction</DocHeading>
<DocText>Background paragraph.</DocText>
</DocSection>
</DocPage>See also: DocRow · DocColumn
DocRow component
(props: { children: VNodeChild }) => VNodeChildHorizontal layout container — children flow inline with a fixed 8px gap. Use for side-by-side content (label + value pairs, columns of metadata, button rows). Layout-only — no user-configurable props on this primitive; for columns with custom widths use DocColumn inside.
Example
<DocRow>
<DocText>Name:</DocText>
<DocText>Aisha Patel</DocText>
</DocRow>See also: DocColumn · DocSection
DocColumn component
(props: { width?: number | string; children: VNodeChild }) => VNodeChildA column inside a row layout. Optional width controls the column's share of the row — accepts a number (interpreted as pixels) or a string ("50%", "1fr"). When omitted, columns share available width equally. Most common shape is <DocRow><DocColumn width="30%" /> <DocColumn width="70%" /></DocRow>.
Example
<DocRow>
<DocColumn width="30%">
<DocText>Label</DocText>
</DocColumn>
<DocColumn width="70%">
<DocText>Value</DocText>
</DocColumn>
</DocRow>See also: DocRow · DocSection
DocHeading component
(props: { level?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; children: VNodeChild }) => VNodeChildHeading text — level ("h1" through "h6") controls both visual size and the semantic level emitted to outputs (HTML <h1>...<h6>, DOCX heading styles, Markdown #...######). Default level is "h1". Used for document structure that downstream tooling can build a TOC from.
Example
<DocHeading level="h1">Quarterly Report</DocHeading>
<DocHeading level="h2">Q4 Results</DocHeading>
<DocHeading level="h3">Revenue Breakdown</DocHeading>See also: DocText · DocSection
DocText component
(props: { children: VNodeChild }) => VNodeChildParagraph / inline text. The most common primitive — wraps any text content for the document. Children may be string literals OR signal accessors ({() => store.field()}) for reactive content. Visual styling (font weight, variant) is controlled via rocketstyle dimension props on the wrapping component definition.
Example
<DocText>Static paragraph content.</DocText>
// Reactive children
<DocText>{`Hello, ${user().name}`}</DocText>See also: DocHeading · DocLink
DocLink component
(props: { href?: string; children: VNodeChild }) => VNodeChildHyperlink within text. href is the URL — defaults to "#". Outputs that support hyperlinks (HTML, PDF, DOCX, email) render this as a clickable link; flat outputs (plain text, certain Slack variants) render the link target inline as text (href).
Example
<DocText>
Read more on
<DocLink href="https://pyreon.dev">our blog</DocLink>
for the latest releases.
</DocText>See also: DocText
DocImage component
(props: { src: string; alt?: string; width?: number; height?: number; caption?: string }) => VNodeChildAn image embedded in the document. src is the image URL or data URI. alt is the accessible description (also used as fallback text in non-visual outputs). width / height constrain dimensions in pixels. Optional caption renders a caption beneath the image.
Example
<DocImage
src="/charts/q4-revenue.png"
alt="Revenue grew 23% in Q4"
width={600}
height={400}
caption="Figure 1: Quarterly revenue, 2024-2025"
/>See also: DocCode
DocTable component
(props: { columns: TableColumn[]; rows: TableRow[]; headerStyle?: object; striped?: boolean; bordered?: boolean; caption?: string }) => VNodeChildTabular data. columns defines the header cells (label, key, optional alignment). rows is an array of data rows keyed by column key. striped adds alternating row backgrounds; bordered adds cell borders; caption renders an accessible table caption. Both rows and columns are filtered before reaching the DOM via .attrs(..., { filter: [...] }) because HTMLTableElement.rows / .cells are read-only DOM properties — assignment would crash.
Example
<DocTable
caption="Q4 results by region"
bordered
striped
columns={[
{ key: 'region', label: 'Region', align: 'left' },
{ key: 'revenue', label: 'Revenue', align: 'right' },
{ key: 'growth', label: 'YoY Growth', align: 'right' },
]}
rows={[
{ region: 'NA', revenue: '$12.4M', growth: '+23%' },
{ region: 'EU', revenue: '$8.7M', growth: '+18%' },
{ region: 'APAC', revenue: '$5.1M', growth: '+41%' },
]}
/>Common mistakes
Keying
rowsby position or label — each row object is keyed bycolumn.key, NOT by order. Acolumnsentry whosekeymatches no field in a row renders an EMPTY cell, and a row field with no matching columnkeyis dropped. Keepcolumns[].keyand therows[]object keys in sync.Expecting
columns/rows(andheaderStyle/striped/bordered/caption) to reach the DOM as attributes — they are_documentProps-only, stripped by DocTable's.attrs(…, { filter })before render becauseHTMLTableElement.rowsis a read-only property (assigning it throwsCannot set property rows). Only relevant if you author your OWN table primitive on a<table>base — apply the same filter.
See also: DocList · DocSection
DocList component
(props: { ordered?: boolean; children: VNodeChild }) => VNodeChildBulleted (default) or numbered (ordered) list. Children are typically DocListItem instances. Outputs map this to the right native list type — HTML <ul> / <ol>, Markdown - / 1., DOCX list styles.
Example
<DocList>
<DocListItem>First bullet</DocListItem>
<DocListItem>Second bullet</DocListItem>
</DocList>
<DocList ordered>
<DocListItem>First step</DocListItem>
<DocListItem>Second step</DocListItem>
</DocList>Common mistakes
Setting
orderedonDocListItem— it does nothing. The marker (bullet vs number) is decided by the PARENTDocList'sorderedprop, which sets the listtag(ul/ol);DocListItemcarries no marker info (_documentProps: {}).Putting raw text or a bare
DocTextdirectly underDocListinstead of wrapping each entry inDocListItem— list entries come fromDocListItem(_documentType: "list-item"); a non-item child is not a list row. Nest aDocListINSIDE aDocListItemfor sublists.
See also: DocListItem
DocListItem component
(props: { children: VNodeChild }) => VNodeChildSingle item inside a DocList. Children may be plain text, DocText, nested DocList for sublists, or any other inline primitive. Visual marker (bullet vs number) is decided by the parent list's ordered prop, not by the item.
Example
<DocList>
<DocListItem>Top-level item</DocListItem>
<DocListItem>
Item with nested list
<DocList>
<DocListItem>Nested A</DocListItem>
<DocListItem>Nested B</DocListItem>
</DocList>
</DocListItem>
</DocList>See also: DocList
DocCode component
(props: { language?: string; children: VNodeChild }) => VNodeChildMonospace code block. Optional language hint enables syntax highlighting in outputs that support it (HTML via Prism / Shiki, Markdown fenced code blocks with language tag). Whitespace is preserved verbatim — pass code as a single string child to keep newlines.
Example
<DocCode language="typescript">{
`const flow = createFlow({
nodes: [{ id: '1', position: { x: 0, y: 0 } }],
edges: [],
})`
}</DocCode>See also: DocText
DocDivider component
(props: { color?: string; thickness?: number }) => VNodeChildHorizontal rule — visual section separator. color controls the line color (any CSS color string); thickness controls the line thickness in pixels. Outputs map this to native dividers — HTML <hr>, Markdown ---, DOCX horizontal rule.
Example
<DocText>Above the divider.</DocText>
<DocDivider color="#e5e7eb" thickness={1} />
<DocText>Below the divider.</DocText>See also: DocSpacer
DocSpacer component
(props: { height?: number }) => VNodeChildVertical whitespace — adds a blank vertical gap. height is in pixels (default 16). Use to space out content beyond what DocSection / DocPage margins provide. In flow outputs this becomes a styled blank block; in plain-text outputs, a sequence of newlines.
Example
<DocSection>
<DocHeading level="h2">Section A</DocHeading>
<DocText>Content...</DocText>
<DocSpacer height={32} />
<DocHeading level="h2">Section B</DocHeading>
<DocText>More content...</DocText>
</DocSection>See also: DocDivider
DocButton component
(props: { href?: string; children: VNodeChild }) => VNodeChildCall-to-action button. Renders as a styled clickable element in HTML / email outputs (mail-safe button table layout for email), and as a labeled link in PDF / DOCX. href is the action URL — defaults to "#". Visual style (variant) is controlled via rocketstyle dimensions on the component definition.
Example
<DocButton href="https://pyreon.dev/signup">
Get started
</DocButton>See also: DocLink
DocQuote component
(props: { borderColor?: string; children: VNodeChild }) => VNodeChildBlock quote — sets off a quoted passage with an indented left border. borderColor controls the indicator stripe (any CSS color). Outputs map this to native quote styling — HTML <blockquote>, Markdown > ..., DOCX quote style.
Example
<DocQuote borderColor="#3b82f6">
<DocText>"The best way to predict the future is to build it."</DocText>
<DocText>— Aisha Patel, Q4 keynote</DocText>
</DocQuote>See also: DocText
DocPageBreak component
() => VNodeChildExplicit page boundary inside a DocPage. Forces the renderer to start a new page at this point in paginated outputs (PDF, DOCX). In flow outputs (HTML, Markdown), it renders as visible whitespace or is omitted entirely. Use for explicit pagination control beyond what DocPage boundaries already provide.
Example
<DocPage>
<DocHeading level="h1">Section 1</DocHeading>
<DocText>...long content...</DocText>
<DocPageBreak />
<DocHeading level="h1">Section 2 — new page</DocHeading>
</DocPage>See also: DocPage
DocumentPreview component
DocumentPreview(props: { size?: 'A4' | 'A3' | 'A5' | 'letter' | 'legal'; showPageBreaks?: boolean; children }) => VNodeA paper-sized PREVIEW wrapper for a document-primitive tree — it renders the Doc* subtree as centered white pages (gray backdrop + drop-shadow) at real paper dimensions so you can preview a document in the browser before exporting. size picks the page format ('A4' default, plus A3/A5/letter/legal); showPageBreaks toggles page-break visualization. It carries the _documentType: 'document' static, so it ALSO serves as the extraction root — extractDocumentTree treats it like a DocDocument, so you typically do not nest a separate <DocDocument> inside it.
Example
<DocumentPreview size="A4">
<DocPage>
<DocHeading level="h1">Report</DocHeading>
<DocText>Preview me at real A4 dimensions.</DocText>
</DocPage>
</DocumentPreview>Common mistakes
Treating it as export-only chrome — it is a BROWSER preview wrapper (paper backdrop + page sizing); the actual export runs through
createDocumentExport/extractDocumentTree.Nesting a
<DocDocument>inside it —DocumentPreviewalready carries_documentType: 'document'and is the extraction root, so wrapping it in another document root double-nests the document node.Passing a size it does not define — only 'A4' / 'A3' / 'A5' / 'letter' / 'legal' map to paper dimensions; an unknown size falls back to the base with no page sizing.
See also: DocDocument · createDocumentExport
documentTheme constant
documentTheme: { colors; fonts; sizes; spacing } // type DocumentTheme = typeof documentThemeThe default theme object for document styling/export — a plain nested config of colors (primary / text / background / border / header / striped-row), fonts (heading / body / mono font stacks), sizes (h1–h6 + body / caption / label point sizes), and spacing (xs–xl). Reference it or spread-override it when customizing how a document renders and exports. Exported alongside the DocumentTheme type (typeof documentTheme).
Example
import { documentTheme } from '@pyreon/document-primitives'
const brandTheme = {
...documentTheme,
colors: { ...documentTheme.colors, primary: '#0ea5e9' },
}Common mistakes
Mutating
documentThemein place — it is a shared module-level object; spread-clone it ({ ...documentTheme, ... }) to override, or you change it for every consumer.
See also: DocDocument
Package-level notes
Reactive metadata:
DocDocumenttitle/author/subjectaccept either strings or() => stringaccessors. Function values are stored in_documentPropsand resolved byextractDocumentTreeat extraction time, so each export click reads the LIVE value from any underlying signal — noconst initial = get()workaround needed.
PR #197 framework fix: Before PR #197,
extractDocumentTreeonly looked at the JSX vnode's direct props for_documentProps— but rocketstyle's attrs HOC stamps that field AFTER the component runs, so every real primitive's metadata was silently dropped during export. The extractor now CALLS the component function to capture the post-attrs vnode and reads_documentPropsfrom there.
DocTable read-only DOM property collision:
HTMLTableElement.rowsand.cellsare read-only DOM properties — assigning to them throws.DocTableuses.attrs(callback, { filter: ["rows", "columns", ...] })to strip these props before they reach the DOM. Watch for similar collisions when adding new primitives that accept prop names matching native HTML element properties.