# How the Lexical Rich Text Editor Is Integrated Into DrawDB for Table Descriptions

> Learn how DrawDB integrates the Lexical rich text editor for table descriptions. Discover how formatted text and markdown shortcuts enhance table info. Read more now.

- Repository: [drawDB/drawdb](https://github.com/drawdb-io/drawdb)
- Tags: internals
- Published: 2026-08-14

---

**DrawDB integrates the Lexical rich text editor into the Table Info side-panel by wrapping it in a dedicated React component that replaces the standard text area when users expand the Description section, allowing formatted text, markdown shortcuts, and links to be persisted to the table's comment property.**

DrawDB is an open-source database diagramming tool that stores table metadata—including descriptions—in a `comment` property. When users need to add rich formatting to table descriptions, the application switches from a plain `TextArea` to a Lexical-based editor. According to the drawdb-io/drawdb source code, this integration keeps the underlying data model simple while providing a full-featured editing experience.

## Architecture of the Lexical Integration

The integration consists of three specialized components working together to render the editor, handle formatting commands, and persist data back to the diagram state.

### The RichEditor Component Wrapper

Located at [`src/components/LexicalEditor/RichEditor.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/RichEditor.jsx), this component serves as the primary interface between DrawDB and Lexical. It assembles the necessary plugins and configures the editor instance.

The `RichEditor` function accepts `theme`, `placeholder`, `initialValue`, and `onBlur` props. It returns a `LexicalComposer` configured with an `initialConfig` derived from the table's existing comment. The component mounts several essential plugins:

```jsx
// src/components/LexicalEditor/RichEditor.jsx
export default function RichEditor({ theme, placeholder, initialValue, onBlur }) {
  return (
    <LexicalComposer initialConfig={editorConfig(initialValue)}>
      <ToolbarPlugin theme={theme} />
      <RichTextPlugin
        contentEditable={<ContentEditable className="editor-input" />}
        placeholder={<Placeholder text={placeholder} />}
        ErrorBoundary={LexicalErrorBoundary}
      />
      <HistoryPlugin />
      <AutoFocusPlugin />
      <LinkPlugin />
      <ListPlugin />
      <AutoLinkPlugin />
      <ListMaxIndentLevelPlugin maxDepth={7} />
      <MarkdownShortcutPlugin transformers={TRANSFORMERS} />
      <ClearEditorPlugin />
    </LexicalComposer>
  );
}

```

This architecture allows DrawDB to support **markdown shortcuts**, **auto-linking**, **list handling**, and **code highlighting** without modifying the core data structure.

### ToolbarPlugin for Formatting Controls

The formatting toolbar is provided by [`src/components/LexicalEditor/ToolbarPlugin.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/ToolbarPlugin.jsx). This component uses the `useLexicalComposerContext` hook to access the editor instance and dispatch formatting commands.

The plugin renders buttons for bold, italic, links, headings, and lists. When users click a formatting button, the plugin dispatches specific Lexical commands:

```jsx
// src/components/LexicalEditor/ToolbarPlugin.jsx
function ToolbarPlugin({ theme }) {
  const [editor] = useLexicalComposerContext();

  const formatText = (format) => {
    editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
  };

  return (
    <div className={`toolbar ${theme}`}>
      <Button icon="bold" onClick={() => formatText("bold")} />
      <Button icon="italic" onClick={() => formatText("italic")} />
      <Button icon="link" onClick={() => 
        editor.dispatchCommand(INSERT_LINK_COMMAND)} 
      />
      {/* Additional formatting controls */}
    </div>
  );
}

```

### TableInfo Component Integration

The `TableInfo` component at [`src/components/EditorSidePanel/TablesTab/TableInfo.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorSidePanel/TablesTab/TableInfo.jsx) hosts the description panel. It conditionally renders the `RichEditor` when the user expands the Description section.

The component manages local state through `showComment` and `commentActiveKey` to control visibility. When the panel opens, it passes the current table comment as `initialValue` and handles persistence via the `onBlur` callback:

```jsx
// src/components/EditorSidePanel/TablesTab/TableInfo.jsx
{showComment && (
  <Collapse activeKey={commentActiveKey} onChange={setCommentActiveKey}>
    <Panel header={t("description")} itemKey="1">
      <RichEditor
        theme={layout.theme}
        placeholder={t("table_description")}
        initialValue={data.comment}
        onBlur={(newComment) =>
          updateTable(data.id, { comment: newComment })
        }
      />
    </Panel>
  </Collapse>
)}

```

## Data Flow and Persistence

The Lexical rich text editor integration follows a strict unidirectional data flow that preserves DrawDB's undo/redo capabilities and save state management.

### Loading and Initialization

1. **Initial Data**: `TableInfo` receives a table object (`data`) containing the `comment` property from the diagram context.
2. **Panel Activation**: Clicking the Description header triggers `setCommentActiveKey("1")` and `setShowComment(true)`.
3. **Editor Mount**: `RichEditor` initializes with `editorConfig(initialValue)`, converting the plain text comment into Lexical's internal state tree.

### Editing and Saving

4. **User Input**: As users type, Lexical's internal state updates through the mounted plugins. The `HistoryPlugin` tracks changes for undo/redo functionality.
5. **Persistence**: On blur events, the editor state is serialized back to plain text using `editor.getEditorState().read(() => $getRoot().getTextContent())`.
6. **State Update**: The `onBlur` callback invokes `updateTable(data.id, { comment: newValue })`, which triggers:
   - Updates to the undo stack via `setUndoStack`
   - Updates to the redo stack via `setRedoStack`
   - A save state change to `State.SAVING` via `setSaveState`

This flow ensures that the **Lexical rich text editor** can handle complex formatting internally while the DrawDB application continues to treat table descriptions as simple strings in the data model.

## Why DrawDB Chose Lexical

DrawDB selected Lexical for its plugin-based architecture and minimal core footprint. Unlike monolithic editors, Lexical allows DrawDB to import only necessary features—markdown shortcuts, list handling, and code highlighting—keeping the bundle size small. The separation between the editor's internal state and the application's data model means the `comment` property remains a string, avoiding complex migration requirements while still supporting rich text capabilities.

## Key Implementation Files

- [`src/components/LexicalEditor/RichEditor.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/RichEditor.jsx): Wraps Lexical plugins and exposes the editor UI.
- [`src/components/LexicalEditor/ToolbarPlugin.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/ToolbarPlugin.jsx): Provides the formatting toolbar used by the editor.
- [`src/components/EditorSidePanel/TablesTab/TableInfo.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/EditorSidePanel/TablesTab/TableInfo.jsx): Hosts the description panel and integrates `RichEditor` for the table's comment field.
- [`src/context/DiagramContext.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/context/DiagramContext.jsx): Supplies `updateTable` and diagram-state helpers that persist comment changes.

## Summary

- DrawDB stores table descriptions in the `comment` property of table objects, treating them as plain strings in the data model.
- The **Lexical rich text editor** is wrapped in a `RichEditor` component at [`src/components/LexicalEditor/RichEditor.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/RichEditor.jsx) that assembles necessary plugins including `ToolbarPlugin`, `HistoryPlugin`, and `MarkdownShortcutPlugin`.
- `TableInfo` conditionally renders the editor when users expand the Description section, passing the current comment as initial state.
- Changes are persisted on blur events via the `updateTable` hook, which updates the diagram's undo/redo stack and triggers save states.
- This architecture allows rich formatting (bold, italics, links, lists) without changing the underlying database schema or data structures.

## Frequently Asked Questions

### How does DrawDB convert Lexical editor state back to plain text for storage?

DrawDB reads the editor's content using Lexical's `$getRoot().getTextContent()` method inside an editor state read operation. When the `onBlur` event fires in the `RichEditor` component, it serializes the rich text tree to a plain string and passes it to the `updateTable` function, ensuring the `comment` property always contains simple text compatible with the existing data model.

### Can users use markdown shortcuts in the DrawDB table description editor?

Yes. The `RichEditor` component includes `MarkdownShortcutPlugin` with `TRANSFORMERS` enabled, allowing users to type markdown syntax (such as `#` for headings or `-` for lists) to trigger automatic formatting conversion without leaving the keyboard.

### What happens to formatting when exporting or importing diagrams in DrawDB?

Since DrawDB persists only the plain text representation of the description via `$getRoot().getTextContent()`, formatting metadata is not stored in the diagram JSON structure. When a diagram is reopened, the `RichEditor` initializes with this plain text, and users must reapply any formatting. This design choice maintains backward compatibility and keeps the file format simple.

### Where is the toolbar configuration defined for the Lexical editor in DrawDB?

The toolbar is implemented in [`src/components/LexicalEditor/ToolbarPlugin.jsx`](https://github.com/drawdb-io/drawdb/blob/main/src/components/LexicalEditor/ToolbarPlugin.jsx). This component uses the `useLexicalComposerContext` hook to access the editor instance and dispatches commands like `FORMAT_TEXT_COMMAND` and `INSERT_LINK_COMMAND` when users click formatting buttons. The toolbar receives the current theme via props to maintain visual consistency with the rest of the application.