# Document

Binds one form artifact and its data to everything beneath it.

Canonical URL: https://docs.asterdoc.com/components/document/

`Document` is where a composition meets a real artifact: it loads the form
definition and its data once, and everything nested inside it — every
`Field`, `Section`, and `Table` — reads through the context only `Document`
provides. Rendering any of those components outside a `Document` throws.

## Preview


```tsx
import { Document } from "@/components/asterdoc/document";
import { Field } from "@/components/asterdoc/field";
import { Section } from "@/components/asterdoc/section";
import { proposalForm } from "@/examples/proposal";
import { shortProposalData } from "@/examples/proposal-data";

export function DocumentDemo() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="document-demo">
      <Field path="proposalNumber" />
      <Section id="customer" title="Prepared for" className="flex flex-col gap-1">
        <Field path="customer" label={false} className="font-medium text-neutral-900" />
        <Field path="customerAddress" label={false} className="text-neutral-600" />
      </Section>
    </Document>
  );
}
```


## Installation


```bash
npx shadcn@4 add @asterdoc/document
```


```bash
asterdoc add document
```


Installs to components/asterdoc/document.tsx.


## Usage


```tsx
import { Document } from "@/components/asterdoc/document";

<Document artifact={proposalForm} data={shortProposalData} tokens={{ accentColor: "#b45309" }}>
  <Section id="summary" title="Summary">
    <Field path="summary" label={false} />
  </Section>
</Document>
```

| Prop | Type | Optional | Default | Description |
| --- | --- | --- | --- | --- |
| `artifact` | `Form` | No |  | The form artifact this document binds to; every component beneath it reads from it. |
| `data` | `DocumentData` | No |  | The values that fill the artifact's fields. |
| `format` | `FormatOptions` | Yes |  | How values without a matching serializer are formatted, and which locale registry covers the rest. |
| `tokens` | `DocumentTokensInput` | Yes |  | Tenant branding applied to this document and every component nested inside it. |
| `id` | `string` | Yes |  | Value for the wrapping element's `data-document-id`; falls back to the artifact name.
Keep ids remain document-global, so repeated compositions must give their keeps distinct ids. |
| `className` | `string` | Yes |  | Classes for the document's wrapping element. |
| `children` | `ReactNode` | No |  | The document's composed content. |


## Composition

A `Document` is its own root when nothing wraps it, exactly like a
[`Bundle`](/components/bundle) around one. Nested inside a `Bundle`, it
inherits that bundle's tokens instead of resolving its own, and only the
outermost of the two may set `pageSize`, `marginPx`, `dir`, `lang`, or `typography` — a
nested `Document` that tries throws, since those tokens decide the paper
every document in the composition shares.

`format` swaps in a different formatter or serializer registry: the same one
every [`Field`](/components/field) and [`Table`](/components/table) beneath
this `Document` reads through, so changing it here changes how every value
underneath prints, without touching any of those components themselves.

`Document` brings along no [`Keep Together`](/components/keep-together) of
its own — `Field`, a titled `Section`, and `Table` each bring their own. Its
job is the context those keeps read, not a pagination unit itself.

A composition with more than one `Document` belongs inside one
[`Bundle`](/components/bundle); pagination itself starts once the whole tree
is rendered inside [`Pages`](/components/pages).

The root's default classes follow the [typography](/components/typography)
token: `scale` steps its text size and leading, `flow` its gap between blocks.
A `className` you pass replaces them and wins.

## Variants

`Document` takes an optional `id`, `format`, `tokens`, and a `className` that
replaces its default column layout.

### Custom layout

A custom `className` lays the document's children out in a row instead of a
column.


```tsx
import { Document } from "@/components/asterdoc/document";
import { Field } from "@/components/asterdoc/field";
import { proposalForm } from "@/examples/proposal";
import { shortProposalData } from "@/examples/proposal-data";

export function DocumentVariantCustomLayout() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="document-variant-custom-layout" className="flex flex-row gap-6 text-sm text-neutral-900">
      <Field path="provider" />
      <Field path="customer" />
    </Document>
  );
}
```


### Custom format

A `format` override changes how a field type prints — here, an organization
prints only its name instead of the default's fuller detail.


```tsx
import { createFormatter } from "@asterdoc/format";
import { Document } from "@/components/asterdoc/document";
import { Field } from "@/components/asterdoc/field";
import { proposalForm } from "@/examples/proposal";
import { shortProposalData } from "@/examples/proposal-data";

const nameOnlyFormatter = createFormatter({
  overrides: {
    organization: (value) => String(value?.name ?? ""),
  },
});

export function DocumentVariantCustomFormat() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="document-variant-custom-format" format={{ formatter: nameOnlyFormatter }}>
      <Field path="provider" />
    </Document>
  );
}
```


### Branded tokens

`tokens` cascades to every component nested inside — here, a titled
section's heading picks up the accent color.


```tsx
import { Document } from "@/components/asterdoc/document";
import { Field } from "@/components/asterdoc/field";
import { Section } from "@/components/asterdoc/section";
import { proposalForm } from "@/examples/proposal";
import { shortProposalData } from "@/examples/proposal-data";

export function DocumentVariantBrandedTokens() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="document-variant-branded-tokens" tokens={{ accentColor: "#b45309" }}>
      <Section id="summary" title="Summary">
        <Field path="summary" label={false} />
      </Section>
    </Document>
  );
}
```
