Introduction
Lexical is an extensible text editor framework for the web, built for reliability, accessibility, and performance. It gives you a small, dependency-free core and a set of optional packages that you compose into the editor you need, from a plain-text input with mentions to a collaborative rich-text document editor.
Lexical attaches to a contenteditable element and keeps its own model of the
document. You read and change that model with Lexical's APIs, and Lexical
takes care of keeping the DOM, the selection, and the browser's many
contenteditable quirks in sync. Most code never touches the DOM directly;
the main exception is custom nodes, which define how they render.
What can you build?
- Plain-text inputs that need more than a
<textarea>: mentions, hashtags, links, custom emoji. - Rich-text editors for comments, posts, and messages.
- Full document editors with tables, lists, code blocks, and images, for a CMS or a notes app.
- Real-time collaborative editing with shared content and remote cursors, using the Yjs integration.
Lexical supplies the editing infrastructure. Your application supplies the layout, toolbars, menus, styling, and storage.
Lexical is an open-source community project. It began at Meta, and its contributors today include Meta engineers alongside many volunteers and developers from other companies. It powers text editing in Meta's web products and at Bloomberg, and is also the editor behind Payload CMS, Supabase, Proton Docs, MDXEditor, Sveltia CMS, Dify, RAGFlow, DeepSeek Harness, Paperclip, and Lexxy. To see what it can do, try the playground.
How it fits together
The lexical package is the core: the editor, the editor state, the base
node types, selection, commands, and the DOM reconciler. Everything else is an
optional package built on top of it, so an application only includes the
features it uses. The core is framework-agnostic, and @lexical/react
provides React bindings.
Features are added to an editor as extensions. An extension bundles everything a feature needs (its nodes, configuration, commands, and listeners, plus any extensions it depends on) so it can be added in one place:
import {buildEditorFromExtensions} from '@lexical/extension';
import {HistoryExtension} from '@lexical/history';
import {RichTextExtension} from '@lexical/rich-text';
import {defineExtension} from 'lexical';
const editor = buildEditorFromExtensions(
defineExtension({
dependencies: [RichTextExtension, HistoryExtension],
name: '@my-app/editor',
namespace: 'my-app',
}),
);
editor.setRootElement(document.getElementById('editor'));
In React, LexicalExtensionComposer does the same
job. The Quick Start and
React guide walk through a complete setup.
Core concepts
These are the ideas the rest of the documentation builds on. Each one links to a page with the details.
Editor
The editor wires everything together. It owns the current editor state,
attaches to a root DOM element, and is where you register nodes, listeners,
transforms, and commands. You usually create it with
buildEditorFromExtensions or through the React bindings rather than calling
createEditor() yourself.
Editor state
An editor state is an immutable snapshot of the
document: a tree of nodes under a single RootNode,
plus a selection. The editor state, not the DOM, is
the source of truth. Its toJSON() output is how you save and restore content.
A DOM-like document tree
Lexical's node tree is shaped like the HTML it renders. A paragraph contains its text, and a link is an element that contains the text it wraps. See Document Model, and Compared with ProseMirror for how this differs from a mark-based editor.
Reading and updating editor state
All reads and writes happen inside a synchronous callback, such as
editor.update(fn) or editor.read('force-commit', fn). Functions whose
names start with $, such as $getRoot(), only work inside those callbacks,
in the same way React Hooks only work while a component renders. See
Editor State.
Updates and the DOM reconciler
Updates change a pending copy of the editor state. Updates in the same tick are batched, and when the batch commits, the reconciler patches only the DOM that belongs to changed nodes. Lexical also watches for DOM changes made outside it and keeps or reverts them. Updates walks through each phase, with an interactive example.
Commands, transforms, and listeners
Most editor behavior is built from three kinds of hooks. Commands carry user input and your own actions to handlers in priority order. Node transforms run during an update to keep the document in shape. Listeners react after an update is committed.
Serialization and running on a server
Content converts to and from JSON, HTML, and Markdown; see Serialization. An editor with no root element does no DOM work at all, so the same code can run on a server or in tests; see Running Without a Browser.
Get started
- Quick Start builds an editor without a framework, and Getting Started with React does the same in React.
- Lexical Extensions and Included Extensions cover how to add features.
- The playground shows many features working together.