Frontend Development 18 min read

An Overview of Lexical: Facebook’s Extensible Web Text‑Editor Framework

Lexical is a highly extensible, framework‑agnostic JavaScript text‑editing library created by Facebook, offering a lightweight core, plugin architecture, custom nodes, themes, and state management for building rich‑text, collaborative, and native‑like editors across web applications.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
An Overview of Lexical: Facebook’s Extensible Web Text‑Editor Framework

Lexical is a Facebook‑originated, JavaScript‑based web text‑editing framework designed for high extensibility, reliability, ease of use, and performance. It is framework‑agnostic, working independently of React or Vue, though its close integration with React makes a React version readily available.

The core package is only about 22 KB; additional functionality is provided through plugin modules that can be lazy‑loaded when the user interacts with the editor, improving performance.

Capabilities

Lexical enables developers to create various editors, such as a plain‑text editor with mentions, emojis, links and hashtags; a rich‑text editor for blogs or chat applications; and a WYSIWYG editor for CMS platforms. Currently only a web version is shipped, with a native version planned.

Core Concepts

The architecture revolves around state , transform , listener , and plugin . An Editor instance connects a contenteditable DOM element to the editor’s internal EditorState via createEditor() .

Editor Instance

An Editor is created with createEditor({ namespace: 'MyEditor', theme: {...} }) . The instance binds to a DOM element, registers events, and exposes methods such as update() and getEditorState() .

Theme Customisation

Developers can define custom CSS class names for DOM elements and supply a stylesheet. Example configuration:

import {createEditor} from 'lexical';
const editor = createEditor({
  namespace: 'MyEditor',
  theme: {
    ltr: 'ltr',
    rtl: 'rtl',
    paragraph: 'editor-paragraph'
  }
});

Corresponding CSS:

.ltr { text-align: left; }
.rtl { text-align: right; }
.editor-paragraph { margin: 0 0 15px 0; position: relative; }

Editor States

The editor’s content is stored as an immutable EditorState consisting of a node tree and a Selection object. Updates are performed inside editor.update(() => { … }) , and the latest state can be read with editor.getEditorState().read(() => { … }) . The state can be serialized to JSON, parsed back with editor.parseEditorState() , and set with editor.setEditorState() . Discrete updates can be forced with {discrete: true} to avoid asynchronous timing issues.

DOM Rendering

Lexical diff‑compares successive EditorState snapshots and only re‑renders changed parts, similar to a virtual DOM, which yields excellent performance.

Event Listeners, Commands, and Plugins

Updates can be observed via editor.registerUpdateListener(({editorState}) => { … }) . Custom commands are created with createCommand() and dispatched with editor.dispatchCommand() . Plugins are simple functions that receive the Editor instance and return a cleanup function.

Node Types

Lexical defines several node classes that form the editor’s data model:

RootNode : the top‑level container for the editable area.

LineBreakNode : represents line breaks without using raw \n .

ElementNode : parent node for structural elements such as paragraphs, headings, and links.

TextNode : leaf node that stores text and formatting flags (bold, italic, underline, code, etc.).

DecoratorNode : wraps arbitrary React or DOM components (e.g., video players).

Only ElementNode , TextNode , and DecoratorNode are intended for public extension.

Custom Nodes and Overrides

Developers can extend ElementNode to create custom paragraph types, override built‑in nodes via the nodes config array, and register node transforms for efficient batch updates. Example of a custom paragraph node:

import {ElementNode, LexicalNode} from 'lexical';
export class CustomParagraph extends ElementNode {
  static getType() { return 'custom-paragraph'; }
  static clone(node) { return new CustomParagraph(node.__key); }
  createDOM() { const dom = document.createElement('p'); return dom; }
  updateDOM(prevNode, dom) { return false; }
}

Node overrides are declared like:

const editorConfig = {
  nodes: [
    CustomParagraph,
    { replace: ParagraphNode, with: (node) => new CustomParagraph() }
  ]
};

Node Transforms

Transforms allow fast, single‑pass modifications of the node tree. For example, turning the word "congrats" blue:

editor.registerNodeTransform(TextNode, textNode => {
  if (textNode.getTextContent() === 'congrats') {
    textNode.toggleFormat('color'); // pseudo‑code for illustration
  }
});

Example Application

The article concludes with a complete vanilla‑JS example that creates an editor, registers rich‑text, history, and drag‑and‑drop plugins, and displays the serialized editor state in a textarea. The example demonstrates theme configuration, node creation ( $createHeadingNode , $createParagraphNode ), and update listeners.

Overall, Lexical provides a modular, performant foundation for building sophisticated web‑based text editors without being tied to a specific UI framework.

frontendJavaScriptPlugin ArchitectureRich TextLexicalCustom NodesWeb Editor
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.