Skip to main content

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 value string: 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 ```diagram skeleton 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 ./markdown entry without the canvas; every chrome string translatable through labels
  • 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 (![alt](src)) 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:

Loading editor

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​

PageCovers
EditorProps, modes, the controlled value, outline and folding, shortcuts, form integration
ToolbarDefault toolbar, extending it, compound components, the headless useMarkdownEditor() hook
TablesGFM round-trip, rows and columns, keyboard, density, settings markers
LayoutMax text width and block width (full / text / content)
Code blocksBuilt-in languages, colours, registering a grammar
DiagramsThe drawing canvas, the ```diagram skeleton, the ```drawing format, Mermaid, ink style
ThemingCSS entry points, dark mode, block classes, custom properties
AI authoringGenerating documents and diagrams programmatically
APIEvery export
ChangelogVersion history