Skip to main content

Rich Text Editor

Tiptap-based rich text editor with a toolbar for formatting, alignment, lists, and tables.

Basic usage

Use with zaf to embed in a block or form schema:

import { RichTextEditor } from '@repo/cms'
import { zaf } from '@repo/form'
import { z } from 'zod'

const schema = z.object({
content: zaf(RichTextEditor.schema, { component: RichTextEditor.Root }),
})

The editor stores content as Tiptap JSONContent -- an array of node objects.

Schema

RichTextEditor.schema validates that content has at least one node:

// z.object({ content: z.array(z.any()).min(1) }).passthrough()
import { RichTextEditor } from '@repo/cms'

const editorSchema = RichTextEditor.schema

Tiptap extensions

RichTextEditor.tiptapExtensions is the default extension set:

ExtensionCapability
StarterKitBold, italic, headings, paragraphs, lists, code, blockquote
TextAlignLeft, center, right alignment on headings and paragraphs
UnderlineUnderline formatting
TableResizable tables
TableCellTable cell support
TableHeaderTable header cells
TableRowTable rows

Toolbar

The editor toolbar includes:

  • Text type selector (Paragraph, H1, H2, H3)
  • Bold, Italic, Underline
  • Undo / Redo
  • Alignment (left, center, right)
  • Bullet list / Ordered list
  • Table controls (insert, add/remove rows and columns, toggle header)

ActionButton

RichTextEditor.ActionButton is the toolbar button component used internally. It accepts an icon, active state, and standard button props:

<script lang="ts">
import { RichTextEditor } from '@repo/cms'
</script>

<RichTextEditor.ActionButton
active={editorState.bold}
onclick={() => editor?.chain().focus().toggleBold().run()}
icon={BoldIcon}
/>

Integration with zaf

The editor works as a custom zaf component. In a form, it auto-renders as a rich text field:

const blogSchema = z.object({
title: z.string(),
body: zaf(RichTextEditor.schema, { component: RichTextEditor.Root }),
})

The value binds to JSONContent -- the editor reads it on mount and writes back on every change.