Editor
import { MarkdownEditor } from '@zuilib/text-editor'
<MarkdownEditor value={value} onChange={setValue} mode="edit-md" outline />
Props
MarkdownEditorProps:
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | '' | Markdown source (controlled) |
onChange | (value: string) => void | Called on every edit with the new markdown | |
mode | 'edit-md' | 'edit-raw' | 'view' | 'edit-md' | Editing surface, see below |
placeholder | string | 'Start writing...' | Empty-state hint |
readOnly | boolean | false | Disables editing in the Lexical modes |
autoFocus | boolean | false | Focus on mount |
className | string | Root wrapper class | |
toolbar | boolean | (items) => ReactNode | true | Formatting / insert toolbar in edit-md; the function form customises it (Toolbar) |
outline | boolean | false | Table-of-contents sidebar |
foldable | boolean | true | Collapse sections under headings |
measure | string | Max width of the text column as a CSS length, '48rem' recommended (Layout) | |
defaultBlockWidth | { table?, drawing? } | BlockWidth written into tables / drawings the toolbar inserts (Layout) | |
drawingStyle | 'clean' | 'ink' | 'clean' | Diagram rendering style (Diagrams) |
Modes
mode | UI | Notes |
|---|---|---|
'edit-md' | Lexical rich editor | Shortcuts, toolbar, tables, canvas |
'edit-raw' | Plain <textarea> | Direct markdown source editing |
'view' | Read-only render | Outline and folding still work; the canvas renders read-only |
All three are driven by the same value string, so switching modes is a
prop change. Switching from edit-raw to edit-md re-mounts Lexical with
the latest text.
The controlled value
value is the source of truth. On every edit the editor serialises the
document back to markdown and calls onChange; store that string and pass
it back in. Nothing is kept in the editor that the markdown cannot express:
| Content | Markdown |
|---|---|
| Headings, lists, quotes, links, emphasis | CommonMark |
| Checklists | - [ ] / - [x] |
| Tables | GFM pipe tables; non-default settings as <!-- width: …; density: … --> on the line above (Tables) |
| Fenced code | ```lang fences; untagged blocks export as bare fences (Code blocks) |
| Diagrams | ```drawing fenced JSON, or a ```diagram skeleton on input (Diagrams) |
| Frontmatter | A leading --- block, kept verbatim |
Markdown shortcuts
In edit-md mode the usual markdown shortcuts apply while typing:
| Type | Result |
|---|---|
# … ###### | Heading 1–6 |
- or * | Bullet list |
1. | Numbered list |
- [ ] | Checklist item |
> | Blockquote |
``` (optionally with a language) then Enter | Fenced code block |
| a | b | | Table |
**bold**, *italic*, ~~strike~~, `code` | Inline formatting |
[text](url) | Link |
Outline and folding
outline docks a collapsible table-of-contents sidebar: a live heading
list indented by level, click to scroll, current section highlighted. It is
pure UI, leaves the document untouched, and is available in edit-md and view.
<MarkdownEditor value={value} onChange={setValue} outline />
foldable (default on) shows a chevron in the gutter of each heading on
hover; clicking it collapses the section until the next heading of the same
or higher level. Folding never changes the markdown, fold state resets on
remount, and a folded section expands again if the caret enters it.
Frontmatter
A document that starts with a --- block keeps it as a frontmatter block:
rendered as a distinct block at the top, editable, and emitted verbatim.
---
title: Quarterly review
status: draft
---
# Quarterly review
Form integration
The editor is a controlled input, so it drops into any form library:
<FormField control={form.control} name="body">
{({ field }) => (
<MarkdownEditor value={field.value} onChange={field.onChange} />
)}
</FormField>
Sizing
The root (.zui-text-editor) is width: 100%; height: 100% and scrolls its
own content, so give its container a height (a flex child with
min-height: 0, or a fixed height) to get a scrolling editor; leave the
container unconstrained to let the editor grow with the document.