Headings, lists, inline formatting, links, undo/redo.
npx shadcn@latest add https://sdk-components.thesqd.com/r/rich-text-editor.jsonimport { RichTextEditor } from "@/components/ui/rich-text-editor";import * as React from "react";
import { RichTextEditor } from "@/components/ui/rich-text-editor";
export function Example() {
const [value, setValue] = React.useState("<p>Hello <strong>Squad</strong>.</p>");
return (
<RichTextEditor
value={value}
onChange={setValue}
placeholder="Start writing…"
/>
);
}RichTextEditor (TipTap-backed)
├── Toolbar (top or bottom)
│ ├── Headings (H1/H2/H3)
│ ├── Bold / Italic / Underline / Strike / Code
│ ├── Lists (bullet / ordered)
│ ├── Blockquote
│ ├── Link (popover)
│ ├── Undo / Redo
│ ├── Spell-check popover (optional, retext stack lazy-loaded)
│ └── Dictation mic (optional, Web Speech API)
├── EditorContent (contenteditable)
└── Footer (optional character count)Headings, lists, inline formatting, links, undo/redo.
Flat ClickUp-style description — markdown in/out, highlight text for a floating toolbar.
Enforced via @tiptap/extension-character-count.
Content is required
Markdown-style formatting is supported.
Web Speech API mic in the toolbar — Chrome/Edge only; hides itself elsewhere.
Lazy-loaded retext stack underlines misspellings, repeated words, a/an mistakes, and more. Hover an underline for the message.
We took your notes from the call and tightened the layout. Headlines are bigger, and the key date is highlighted so nobody misses it.
Review the three concepts
Leave a pin on anything that feels off
Approve the one you love
Confirm the go-live date
Heads up: the print deadline is Friday — changes after that push the run a week.
==highlighted==
++underlined++
[color value="green"]coloured[/color] gray red orange yellow green blue purple pink
[button href="https://…"]Label[/button]
:::callout {type="warning"} default brand info success warning destructive
Any blocks — **marks**, lists, headings.
:::
> [!NOTE] GitHub alerts read as callouts tooRichTextEditor
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled HTML value. Pair with `onChange`. |
defaultValue | string | — | Uncontrolled initial HTML. |
onChange | (html: string) => void | — | Fires whenever the editor content changes. |
placeholder | string | — | Empty-state placeholder text. |
disabled | boolean | false | Render as read-only. |
toolbarPosition | "top" | "bottom" | "top" | Pin the toolbar above or below the editable region. |
maxLength | number | — | Cap content via `@tiptap/extension-character-count`; shows a counter footer. |
error | boolean | string | false | Error state. `true` applies the red outline only; a string also renders the inline message (`text-xs mt-1.5 text-red-500`) below the editor. |
caption | ReactNode | — | Helper copy shown below the editor via FieldHint. Error wins when both are present. |
dictation | boolean | false | Mount a Web Speech API mic at the end of the toolbar. Streams transcribed phrases into the editor at the current cursor. Auto-hides in unsupported browsers (Safari, Firefox, mobile WebKit). |
spellCheck | boolean | false | Run a lazy-loaded retext stack (retext-spell + dictionary-en, retext-repeated-words, retext-indefinite-article, retext-redundant-acronyms, retext-contractions, retext-quotes, retext-sentence-spacing) on the editor's text and surface issues in a toolbar popover with a count badge. Each entry shows the offending word + the retext message in a DescriptionList. |
disabledFeatures | RichTextFeature[] | — | Hide toolbar features, e.g. `["h1", "blockquote", "indent"]`. Features: bold, italic, underline, strike, code, h1, h2, h3, bulletList, orderedList, blockquote, link. |
minHeight | number | — | Minimum editable area height in pixels. |
className | string | — | Override or extend the wrapper classes. |
RichTextEditorInline
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled markdown value. Pair with `onChange`. |
defaultValue | string | — | Uncontrolled initial markdown. |
onChange | (markdown: string) => void | — | Fires with the content serialized back to markdown via `editor.getMarkdown()`. |
placeholder | string | "Add a description…" | Empty-state placeholder text. |
disabled | boolean | false | Render as read-only. |
minHeight | number | 80 | Minimum editable area height in pixels. |
collapsible | boolean | false | Collapse the content to `collapsedHeight` with a fade-out gradient and an Expand overlay button, like collapsible code blocks. The toggle only appears when the content overflows. |
defaultCollapsed | boolean | true | Initial collapsed state for `collapsible`. |
collapsedHeight | number | 160 | Collapsed max-height in pixels. |
collapsedLines | number | — | Collapsed height expressed in text lines (24px each). Takes precedence over `collapsedHeight`. |
autoHeight | boolean | false | Fit the collapsed viewport to the space remaining below the editor in the browser viewport (re-measured on resize). Takes precedence over `collapsedLines`/`collapsedHeight`. |
expandOnFocus | boolean | true | Auto-expand when the editor gains focus (click/typing), like ClickUp descriptions. |
disabledFeatures | RichTextFeature[] | — | Hide toolbar features, e.g. `["h1", "blockquote", "indent"]`. Features: bold, italic, underline, strike, code, h1, h2, h3, bulletList, orderedList, blockquote, link, indent (indent is inline-only). |
className | string | — | Override or extend the wrapper classes. |
RichMarkdown
| Prop | Type | Default | Description |
|---|---|---|---|
source | string | — | The markdown to draw — what the Composer wrote. |
document | boolean | — | Templated prose: fill `{{token}}` from `tokens`. Off for somebody's own words. |
tokens | Record<string, string | null | undefined> | — | `{{name}}` → the value to draw. |
resolveMention | (handle: string) => { consumed: string; label: string } | null | — | Draw `@someone` as a chip. Absent, mentions are not looked for. |
renderAttachment | (id: string, name: string) => ReactNode | — | Draw a file placed in the text as ``. Absent, its name is drawn. |
variant | "comment" | "page" | "comment" | `page` draws a document: readable body size, real heading levels with anchors (`richMarkdownOutline(source)` lists them for an "On this page"), and the documentation blocks — steps, tabs, accordions, cards, file trees, titled code blocks — at full size. |
renderCode | ({ lang, text, highlight }) => ReactNode | — | Draw a code block yourself (syntax highlighting). Null keeps the plain block, its title bar and highlighted lines. |
className | string | — | On the wrapper. |