Documentsagenta
Lexical Editor Integration
Lexical Editor Integration
Type
Topic
Status
Published
Created
Jul 9, 2026
Updated
Jul 9, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Lexical Editor Integration#

The agenta web app uses Lexical (v0.30.x) as its primary rich text editor framework. The editor lives at web/oss/src/components/Editor/ and exposes two public surfaces: a self-contained Editor default export and an EditorProvider for cases where you need to embed the editor inside a pre-existing Lexical context.


Component Architecture#

Editor (default export, memo-wrapped)
 └── EditorProvider — wraps LexicalComposer with config from useEditorConfig
      └── EditorInner — registers commands, calls EditorPlugins
           └── EditorPlugins — mounts all Lexical plugins

EditorProvider calls useEditorConfig to lazily build the LexicalComposer initialConfig, then renders <LexicalComposer initialConfig={config}>.

EditorInner is a forwardRef component that handles content hydration and change propagation. It registers the ON_HYDRATE_FROM_REMOTE_CONTENT command to load initial markdown into the editor state.


Markdown ↔ Editor State#

All markdown conversion uses the stock TRANSFORMERS array from @lexical/markdown — no custom transformers are defined.

  • Markdown → editor state (hydration): $convertFromMarkdownString(content, TRANSFORMERS) is called inside the ON_HYDRATE_FROM_REMOTE_CONTENT command handler. The command fires on mount with initialValue and can be re-dispatched by external consumers to reload content.

  • Editor state → markdown (onChange): $convertToMarkdownString(TRANSFORMERS) runs inside the OnChangePlugin callback. The resulting string has backslash escapes stripped (replaceAll(/\\(.)/g, "$1")) before being passed to the onChange prop.

In codeOnly mode, markdown conversion is skipped entirely; content is read as a plain string via $getEditorCodeAsString.


Node Registration (useEditorConfig)#

useEditorConfig lazily imports and registers all node types on first render. Nodes registered for rich-text mode include:

PackageNodes
@lexical/rich-textHeadingNode, QuoteNode
@lexical/listListNode, ListItemNode
@lexical/codeCodeNode, CodeHighlightNode
@lexical/tableTableNode, TableCellNode, TableRowNode
@lexical/hashtagHashtagNode
@lexical/linkAutoLinkNode, LinkNode
@lexical/overflowOverflowNode
@lexical/reactHorizontalRuleNode
@lexical/markMarkNode

When enableTokens is true, custom TokenNode and TokenInputNode are added to this list. When codeOnly is true, custom code block nodes (CodeBlockNode, CodeHighlightNode, CodeLineNode, CodeBlockErrorIndicatorNode) replace the standard Lexical code nodes.

Note on link handling: AutoLinkNode and LinkNode are registered as node types, but no LinkPlugin or AutoLinkPlugin from @lexical/react is mounted. This means link nodes can exist in the editor state (e.g., parsed from markdown) but there is no interactive link-creation UI.


Plugins#

All plugins are lazy-loaded via React.lazy and wrapped in a single <Suspense> boundary with an Ant Design Skeleton fallback. Active plugins and their conditions:

PluginCondition
RichTextPluginAlways on
HistoryPluginAlways on
OnChangePluginAlways on
AutoFocusPluginautoFocus === true
ToolbarPluginshowToolbar && !singleLine && !codeOnly
TokenPluginenableTokens === true
SingleLinePluginsingleLine === true
CodeEditorPlugin + TabIndentationPlugincodeOnly === true
DebugPlugindebug === true

The ToolbarPlugin provides bold, italic, underline, inline-code formatting, and left/center/right alignment via Lexical's FORMAT_TEXT_COMMAND / FORMAT_ELEMENT_COMMAND.


Theme#

The Lexical theme at assets/theme.ts maps syntax-highlight token types (keyword, string, function, etc.) to CSS class names (editor-tokenAttr, editor-tokenFunction, …). No rich-text styling classes (headings, lists, links) are defined here — those are handled by global CSS outside the component.


Key EditorProps#

Defined in types.d.ts:

PropDefaultPurpose
initialValue""Markdown string to hydrate on mount
onChangeReceives { textContent, tokens } on every edit
singleLinefalseRestricts input to one line
codeOnlyfalseSwitches to code editor mode
languageSyntax highlighting language for code mode
enableTokensfalseEnables token nodes
showToolbartrueShows the formatting toolbar
noProviderfalseSkip EditorProvider (use when already inside a LexicalComposer)

Key Files#

FilePurpose
Editor.tsxMain component, markdown conversion logic
hooks/useEditorConfig/index.tsBuilds LexicalComposer config + node list
plugins/index.tsxPlugin orchestration
plugins/toolbar/ToolbarPlugin.tsxFormatting toolbar
assets/theme.tsLexical theme (syntax highlight classes)
types.d.tsTypeScript prop types
Lexical Editor Integration | Dosu