Skip to main content

Editor

import { MarkdownEditor } from '@zuilib/text-editor'

<MarkdownEditor value={value} onChange={setValue} mode="edit-md" outline />

Props

MarkdownEditorProps:

PropTypeDefaultDescription
valuestring''Markdown source (controlled)
onChange(value: string) => voidCalled on every edit with the new markdown
mode'edit-md' | 'edit-raw' | 'view''edit-md'Editing surface, see below
placeholderstring'Start writing...'Empty-state hint
readOnlybooleanfalseDisables editing in the Lexical modes
autoFocusbooleanfalseFocus on mount
classNamestringRoot wrapper class
toolbarboolean | (items) => ReactNodetrueFormatting / insert toolbar in edit-md; the function form customises it (Toolbar)
outlinebooleanfalseTable-of-contents sidebar
foldablebooleantrueCollapse sections under headings
measurestringMax 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

modeUINotes
'edit-md'Lexical rich editorShortcuts, toolbar, tables, canvas
'edit-raw'Plain <textarea>Direct markdown source editing
'view'Read-only renderOutline 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.

Loading editor

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:

ContentMarkdown
Headings, lists, quotes, links, emphasisCommonMark
Checklists- [ ] / - [x]
TablesGFM 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)
FrontmatterA leading --- block, kept verbatim

Markdown shortcuts

In edit-md mode the usual markdown shortcuts apply while typing:

TypeResult
# ###### Heading 1–6
- or * Bullet list
1. Numbered list
- [ ] Checklist item
> Blockquote
``` (optionally with a language) then EnterFenced 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.

Loading editor

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
Loading editor

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.