Text editor
Package: @zuilib/text-editor · 0.13.3
A markdown editor for React, built on Lexical.
This is the first composite in ZUI: a large component built entirely on the token contract. It exists because an editor is the most commonly rebuilt piece of complex UI (tables, code highlighting, diagrams, outline) and the one that most benefits from being built once and themed per product.
The goal is a markdown editor of the standard people now expect from writing tools, in your app with minimal effort. One component, one controlled string, plain markdown out. Tables, diagrams, highlighted code, an outline and section folding work without setup. When the defaults do not fit, every part is replaceable: the toolbar is composable, the editor is exposed through a headless hook, and the transformers and nodes are exported for a Lexical editor of your own. It reads the same tokens as every other ZUI package; a theme applies to it with no editor-specific overrides; the examples on these pages are the real editor.
Features
- Three modes driven by one controlled
valuestring: rich WYSIWYG (edit-rich), raw markdown (edit-raw), read-only render (view) - Markdown shortcuts while typing: headings, lists, checklists, blockquotes, links, fenced code, tables
- Tables: GFM pipe tables edited in place; rows and columns added or removed from hover rails or the keyboard; per-table width and density
- Code blocks highlighted by the package's own lexer (16 grammars); no Prism, highlight.js or Shiki, and themeable colours
- Diagrams: an embedded drawing canvas with eight box shapes, bound
connectors that follow their boxes, auto-routed elbows, multi-select,
colour presets, dark mode and Mermaid export. Generators (LLMs, scripts)
write a coordinate-free
```diagramskeleton that the editor lays out - Max text width and block width: an opt-in readable text column; each table and diagram picks full, text or content width
- Outline sidebar with click-to-scroll and current-section highlight; section folding under any heading, view-layer only
- Toolbar with format, block and insert groups and a link bubble; extend it, move it, or replace it with your own UI through a headless hook
- Extension platform: your own Lexical nodes, markdown transformers and
plugins through props; a
./markdownentry without the canvas; every chrome string translatable throughlabels - YAML frontmatter block support (
---at the top) - Horizontal rules (
---) and GFM footnotes ([^id]cues numbered in reference order, notes gathered at the end, cue ↔ note navigation)
Everything round-trips through the markdown string: tables as GFM,
diagrams as ```drawing fenced JSON, frontmatter as --- blocks, table
settings as one HTML comment above the table. A document written in the
editor opens in any other markdown tool.
What round-trips unchanged: headings, paragraphs, emphasis, inline code,
links, blockquotes, ordered and unordered lists (nested with 2 or 4
spaces on input, exported with 2), checklists, fenced code, GFM tables with their settings
marker, ```drawing blocks, frontmatter, horizontal rules and footnotes.
What does not render as itself: images () show as a !
followed by a link because there is no image node; reference-style links
and raw HTML blocks have no node and show as plain text (the source text is
kept). What
is lossy: a ```diagram skeleton is expanded into a ```drawing block
on import and is not written back as a skeleton.
Installation
pnpm add @zuilib/text-editor
Lexical and ZUI tokens install transitively. React and React DOM remain peers so the editor shares the application's React runtime. Tailwind v4 is optional; the package-owned entry under Styles registers the editor's document typography classes.
Quick start
import { useState } from 'react'
import '@zuilib/text-editor/styles.css'
import { MarkdownEditor } from '@zuilib/text-editor'
function Notes() {
const [value, setValue] = useState('# Hello\n\n- [ ] Try checklists')
return <MarkdownEditor value={value} onValueChange={setValue} outline />
}
The component is controlled: value is the document, onValueChange fires on
every keystroke with the full markdown string, and a different value
passed back in (including '') replaces the document (Editor). Every example on these
pages is the real component; Markdown shows the string it emits:
Styles
For a Tailwind v4 host, import Tailwind and the package-owned source entry once:
@import "tailwindcss";
@import "@zuilib/text-editor/tailwind.css";
Without Tailwind, load @zuilib/text-editor/styles.css; it includes tokens and
the editor chrome. Style the document block classes directly
(Theming).
Dark mode is the ZUI convention: a dark class on <html>.
Small screens and touch
The editor lays out against its own width, not the viewport. Below a 40rem pane the toolbar scrolls sideways (order preserved), the outline becomes a collapsible strip above the document, tables scroll horizontally inside their own box, and drawings wider than the pane scale down to fit.
On touch screens the toolbar buttons, drawing tools and table rail handles
have 44px targets; the fold chevrons and rail handles are always visible,
and a tap on a table rail pins the + / − handle where you tapped (a
second tap acts). A finger on an idle drawing scrolls the page; tap the
canvas to activate it before drawing or dragging.
Anything that pops out of a custom MarkdownEditor.Toolbar (a menu, a
select) should render in a portal: below 40rem the toolbar is a scroll
container and clips overflow.
Guide
| Page | Covers |
|---|---|
| Editor | Props, modes, the controlled value, outline and folding, shortcuts, form integration |
| Toolbar | Default toolbar, extending it, compound components, the headless useMarkdownEditor() hook |
| Tables | GFM round-trip, rows and columns, keyboard, density, settings markers |
| Layout | Max text width and block width (full / text / content) |
| Code blocks | Built-in languages, colours, registering a grammar |
| Diagrams | The drawing canvas, the ```diagram skeleton, the ```drawing format, Mermaid, ink style |
| Theming | CSS entry points, dark mode, block classes, custom properties |
| AI authoring | Generating documents and diagrams programmatically |
| API | Every export |
| Changelog | Version history |