Rich text
<Copy.Rich field="body" />
<Copy.Rich> is the one exception to plain-text output. A client writing a blog
post cannot work with a single unformatted string, so that content is stored as
Markdown.
body: |
We started in **2019** with one idea.
- Ship small
- Ship often
Read the [full story](/about).
The allowlist
Markdown is rendered straight to React elements against a strict allowlist:
| Allowed | |
|---|---|
| Paragraphs | <p> |
| Emphasis | **bold**, *italic* |
| Links | [text](href) |
| Lists | ordered and unordered |
| Headings | # through ###### |
| Blockquotes | > |
| Inline code | `code` |
Anything else — raw HTML, images, tables, code fences with attributes — is dropped, and its text kept. A client pasting styled content from a word processor gets their words, not their markup.
Why there is nothing to sanitise
No HTML string is ever produced. The Markdown AST is walked and React elements
are constructed directly, so there is no dangerouslySetInnerHTML anywhere in
the library and no sanitiser that could miss a case.
That is a structural guarantee rather than a filtering one: a bypass would have to make the Markdown parser emit an allowlisted node type that renders someone else's script, which the node types do not permit. The test suite covers this with XSS bypass attempts.
Link hrefs are still checked — javascript: and data: URLs do not survive.
Styling the output
<Copy.Rich> renders the elements with no classes of its own. Style them from
the parent:
<div className="prose">
<Copy.Rich field="body" />
</div>
In editor mode the output is wrapped in a <div> carrying the editing anchor.
That wrapper does not exist in production, so do not write CSS that depends on
it.
When to use plain text instead
Use <Copy> for anything that is a single string: a heading, a label, a
subtitle, a button. Rich text costs a Markdown parse per field and hands the
client formatting controls they do not need on a hero heading.
A rough rule: if you would be annoyed to find a bulleted list inside it, it is not a rich text field.