OverviewLast updated on
Last updated on
Install the document components into your own project with the shadcn CLI
A document composed in React is a tree of small components: a section, a field, a table, a signature block. What each one renders is a design decision that belongs to you, so they are not shipped as sealed exports. They are shipped as source, in the shadcn registry format: you install a component, the file lands in your project, and from then on it is yours to edit.
What stays in @paradoc/react is the part that is not a design decision — the
document, page, token and signing contexts, the page plan, the measuring pass,
the serializers that turn a value into the text a field prints. The installed
files import it from there. Editing a copied field changes how a field looks; it
cannot change how the document paginates or which paper it is drawn on.
See Installation for setup, once per project, and Typography for the token that sets a document's type scale and block spacing.
The items
| Item | What it is |
|---|---|
document-styles | Tailwind, the structural rules the components need, and every class the typography token can produce. |
bundle | Groups the documents of one composition. |
document | Binds one form artifact and its data to everything beneath it. |
field | One labelled value at a path into the artifact, printed by the artifact's serializers. |
image | A picture at a declared size, from its own bytes or from a source. |
keep-together | The pagination unit. Content inside one is never split across a page break. |
list | An ordered or unordered list whose markers are text, with every item its own pagination unit. |
page-break | Starts a new page at its position, occupying no height of its own. |
page-number | The page being drawn and how many there are, for a document's page furniture. |
pages | Measures the document once and lays it out as paginated sheets. |
paper | One sheet at the document's page geometry, and the fit-to-width hook. |
part | One document of a packet, numbering its own pages and saying where it sits in the packet. |
party | One party of a declared role, with the organization, address, and contact fields beside it, as a block or inline. |
pdf-pages | A PDF part of a packet, painted page by page, or a named attachment when it cannot be painted. |
qr-code | An SVG QR code that links a document to a URL, with configurable size and colors. |
section | A titled container that collapses on pages holding none of its keeps. |
signature | A party's signing block, with the rule the seal measures a signature field from. |
table | A list field as rows, with a header that repeats on every page it continues onto. |
text | Static prose in a named role — heading, body, caption, or small — sized by the typography token. |
totals | The artifact's computed amounts, evaluated by @paradoc/core. |
priced-line-items | Sample line-item arithmetic installed with the worked invoice and purchase-order blocks. |
All 19 components link to a full reference page: a live preview,
installation, usage, the composition it was rendered from, and a few
alternate configurations. document-styles and priced-line-items link
straight to their registry JSON — they're supporting presets, not
components with their own page.
The blocks
A block is a whole document rather than one component: an artifact, a composition that binds it, and sample data to see it with.
| Item | What it is |
|---|---|
purchase-order | A purchase order: the artifact with its React layer, the composition, and a sample order that runs to three pages. |
invoice | An invoice nothing signs: the artifact with its React layer, the composition branded from its own tokens, and two samples — one page, and one whose table runs past two breaks. |
engagement-letter | An engagement letter: the artifact with two signature slots, a composition whose scope of services is numbered prose clauses, and a sample that runs to two pages. |
vendor-packet | A packet of three documents: the purchase order composed live, a filled W-9 painted from its PDF layer, and a certificate of insurance as an annex, whose bytes it ships. |
paradoc add purchase-orderThe composition lands in components/paradoc/ beside the components; the
artifact and its data land in artifacts/paradoc/, because they are not
components. vendor-packet brings purchase-order with it: the order is one of
the packet's parts.
The purchase order's sample runs to three pages, so its table continues across two breaks. The repeated header on a continued page comes from the page plan: you see it in the preview, and in a PDF rendered with the preview's plan passed as hints. The engine paginating on its own does not copy one.
vendor-packet needs @paradoc/essentials, and declares it. Nothing the
block ships imports it, but the packet's second part is the IRS W-9: filling and
sealing it needs that artifact. The block's sample carries the taxpayer's
values, not the form.
import { w9 } from "@paradoc/essentials";
import { vendorPacketData } from "@/artifacts/paradoc/vendor-packet.data";
const parsed = w9.safeParseData(vendorPacketData.taxpayer);
if (!parsed.success) throw new Error("Invalid taxpayer data");
const draft = w9.fill(parsed.data);
const taxpayerPdf = await draft.render({ layer: "pdf" });No resolver is passed: @paradoc/essentials binds each artifact's bundled
resolver when it constructs the form, and every instance derived from it,
including draft, carries it.
The certificate of insurance is not yours to supply: the block installs it as
vendor-packet.annex.ts, a module of bytes, because a block installed into a
browser project has no engine to draw one with. The composition still takes both
PDFs as props.
The index of all of them is /r/registry.json.
For agents
/r/registry.json is machine-readable: name, type, title,
description, dependencies, and the files each item installs, with no page
to scrape. Fetch it to enumerate what is installable, then fetch
/r/{name}.json for one item's exact file list. Both are static JSON with no
auth.
Before rendering a newly authored or edited composition, run
paradoc check <composition-or-artifact-file> — it walks the same tree the
default PDF path walks and reports every unsupported class, unresolved field
path, and missing image in one pass, without producing PDF bytes or needing a
browser. See paradoc check and
paradoc add.
The paradoc-react agent skill packages this page's
content plus the component props, the pagination rule, the verified Tailwind
class subset, and the render and seal flow for an agent authoring a
composition end to end.
Using them
import { Document } from "@/components/paradoc/document";
import { Field } from "@/components/paradoc/field";
import { Pages } from "@/components/paradoc/pages";
import { Section } from "@/components/paradoc/section";
export function Proposal({ artifact, data }) {
return (
<Pages>
<Document artifact={artifact} data={data}>
<Section id="parties" title="Parties">
<Field path="customer" />
<Field path="issuedOn" />
</Section>
</Document>
</Pages>
);
}Field names a path and prints nothing of its own: the label comes from the
artifact and the text comes from the serializer the field's type names. The same
tree renders to PDF through @paradoc/react-pdf.
How the registry is built
The items are generated from the component sources by pnpm registry:build in
the private @paradoc/components workspace, never written by hand. The generator rewrites every import so
the file compiles where it is installed: a sibling item becomes
@/components/paradoc/<name>, and anything else in the package becomes
@paradoc/react, pinned to the public runtime version the item targets. A binding
the package does not export publicly, or an npm package the item does not
declare, fails the build rather than shipping a file you cannot compile, and the
emitted files are installed into a scratch project and type-checked on every
change.