Rich Text

Rich-text fields are stored as structured JSON (a ProseMirror document), not HTML. JSON is portable across web, native and email, safe from HTML injection, and transformable. You render it on your side.

The shape

A rich-text value is a document tree of nodes and marks. The Delivery API returns it under the field key, unchanged:

json
{
  "type": "doc",
  "content": [
    { "type": "heading", "attrs": { "level": 2 },
      "content": [{ "type": "text", "text": "Hello" }] },
    { "type": "paragraph",
      "content": [
        { "type": "text", "text": "Some " },
        { "type": "text", "marks": [{ "type": "bold" }], "text": "bold" },
        { "type": "text", "text": " copy." }
      ] }
  ]
}

Render to HTML (server)

Use TipTap's generateHTML with the same extension set the editor uses. Run it on the server and pass the string down:

bash
npm i @tiptap/html @tiptap/starter-kit
typescript
import { generateHTML } from "@tiptap/html";
import StarterKit from "@tiptap/starter-kit";

export function renderRichText(doc: unknown): string {
  return generateHTML(doc, [StarterKit]);
}
tsx
// In a Next.js server component
const html = renderRichText(entry.data.body);
return <div className="prose" dangerouslySetInnerHTML={{ __html: html }} />;
// REACT RENDERERPrefer real React nodes over dangerouslySetInnerHTML? Use @tiptap/static-renderer to map the JSON to your own components — full control over how each node renders.
// MATCH EXTENSIONSRender with the same extensions the content was authored with. Wriven's editor uses StarterKit (headings, lists, blockquote, code, links). If you add custom nodes later, register them in your renderer too.