Overview

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

ItemWhat it is
document-stylesTailwind, the structural rules the components need, and every class the typography token can produce.
bundleGroups the documents of one composition.
documentBinds one form artifact and its data to everything beneath it.
fieldOne labelled value at a path into the artifact, printed by the artifact's serializers.
imageA picture at a declared size, from its own bytes or from a source.
keep-togetherThe pagination unit. Content inside one is never split across a page break.
listAn ordered or unordered list whose markers are text, with every item its own pagination unit.
page-breakStarts a new page at its position, occupying no height of its own.
page-numberThe page being drawn and how many there are, for a document's page furniture.
pagesMeasures the document once and lays it out as paginated sheets.
paperOne sheet at the document's page geometry, and the fit-to-width hook.
partOne document of a packet, numbering its own pages and saying where it sits in the packet.
partyOne party of a declared role, with the organization, address, and contact fields beside it, as a block or inline.
pdf-pagesA PDF part of a packet, painted page by page, or a named attachment when it cannot be painted.
qr-codeAn SVG QR code that links a document to a URL, with configurable size and colors.
sectionA titled container that collapses on pages holding none of its keeps.
signatureA party's signing block, with the rule the seal measures a signature field from.
tableA list field as rows, with a header that repeats on every page it continues onto.
textStatic prose in a named role — heading, body, caption, or small — sized by the typography token.
totalsThe artifact's computed amounts, evaluated by @paradoc/core.
priced-line-itemsSample 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.

ItemWhat it is
purchase-orderA purchase order: the artifact with its React layer, the composition, and a sample order that runs to three pages.
invoiceAn 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-letterAn 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-packetA 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-order

The 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.

On this page