# Text

Static prose in a named role — heading, body, caption, or small — sized by the document's typography token.

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

`Text` is the prose a document carries that is not a value: a heading, the
paragraph under it, a caption, a line of fine print. Each role takes its size
and leading from the document's [typography](/components/typography) token, so
a heading you install moves with the fields around it instead of staying where
its author left it.

## Preview


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

export function TextDemo() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="text-demo">
      <Text keepId="text-demo:heading" role="heading">
        Scope of services
      </Text>
      <Text keepId="text-demo:body">
        The provider will survey the site, install the equipment listed below, and commission it
        against the acceptance tests the parties agree in writing before work begins.
      </Text>
      <Text keepId="text-demo:caption" role="caption">
        Quantities are estimates until the survey is complete.
      </Text>
    </Document>
  );
}
```


## Installation


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


```bash
asterdoc add text
```


Installs to components/asterdoc/text.tsx.


## Usage


```tsx
import { Text } from "@/components/asterdoc/text";

<Text keepId="text-demo:heading" role="heading">
  Scope of services
</Text>
```

| Prop | Type | Optional | Default | Description |
| --- | --- | --- | --- | --- |
| `keepId` | `string` | No |  | Stable id the page plan tracks this prose by; must be unique within the document. |
| `role` | `TextRole` | Yes | "body" | Which role the prose plays. Sets its size and leading from the document's typography token. |
| `as` | `ElementType` | Yes | "h2" for a heading, "p" for every other role | Element the prose renders as. |
| `className` | `string` | Yes |  | Classes replacing the role's own size, leading, weight and colour. |
| `children` | `ReactNode` | Yes |  | The prose. |


## Composition

There are four roles, and they are the whole vocabulary: `heading` for a
title, `body` for a paragraph, `caption` for a note beside or beneath
something, and `small` for fine print. A role sets a size, a leading, a weight
and a colour; it does not set layout, so a `Text` sits wherever its parent puts
it, most often inside a [`Section`](/components/section).

Every `Text` is a [`Keep Together`](/components/keep-together), which is why
`keepId` is required rather than derived: the page plan names a unit by id, and
an id the composition did not choose is an id it cannot hand to
`renderPdf` as a hint. For the same reason a `Text` never goes inside another
keep — a keep inside a keep is measured twice, and the plan would see two
overlapping units.

`Text` reads nothing from the artifact. Prose that comes from a field is a
[`Field`](/components/field), which prints the value the artifact's serializer
returns.

Passing `className` replaces the role's own classes outright. That is the
opt-out: one paragraph pinned to a size you chose, while the rest of the
document still follows the token.

## Variants

### Heading

`role="heading"` is the largest role. It renders as an `h2` unless `as` says
otherwise.


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

export function TextVariantHeading() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="text-variant-heading">
      <Text keepId="text-variant-heading" role="heading">
        {proposalForm.title}
      </Text>
    </Document>
  );
}
```


### Caption

`role="caption"` is the note beside or beneath something, set smaller and
italic.


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

export function TextVariantCaption() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="text-variant-caption">
      <Text keepId="text-variant-caption" role="caption">
        Prices exclude freight and are held for thirty days.
      </Text>
    </Document>
  );
}
```


### Small

`role="small"` is the fine print a document closes with.


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

export function TextVariantSmall() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="text-variant-small">
      <Text keepId="text-variant-small" role="small">
        This proposal is not an offer capable of acceptance until countersigned by both parties.
      </Text>
    </Document>
  );
}
```


### Custom element

`as` renders a role as a different element without changing its size or
leading — a heading as an `h1` where the document's own outline calls for one.


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

export function TextVariantCustomElement() {
  return (
    <Document artifact={proposalForm} data={shortProposalData} id="text-variant-custom-element">
      <Text keepId="text-variant-custom-element" role="heading" as="h1">
        {proposalForm.title}
      </Text>
    </Document>
  );
}
```
