Lexical is an extensible text editor framework that provides excellent reliability, accessible and performance.
npx @tessl/cli install tessl/npm-lexical@0.34.0Lexical is an extensible JavaScript text editor framework with an emphasis on reliability, accessibility, and performance. It provides a dependency-free editor engine that allows for powerful text editing implementations with immutable state management, plugin architecture, and advanced features like collaborative editing support.
npm install lexicalimport { createEditor, $getRoot, $getSelection } from "lexical";
import { $createTextNode, $createParagraphNode } from "lexical";For CommonJS:
const { createEditor, $getRoot, $getSelection } = require("lexical");import { createEditor, $getRoot, $createParagraphNode, $createTextNode } from "lexical";
// Create an editor instance
const config = {
namespace: 'MyEditor',
theme: {
// Optional theming
},
onError: (error) => {
console.error(error);
},
};
const editor = createEditor(config);
// Associate with a DOM element
const contentEditable = document.getElementById('editor');
editor.setRootElement(contentEditable);
// Update editor content
editor.update(() => {
const root = $getRoot();
const paragraph = $createParagraphNode();
const text = $createTextNode('Hello, Lexical!');
paragraph.append(text);
root.append(paragraph);
});
// Listen for updates
editor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
// Read the current editor state
const root = $getRoot();
console.log(root.getTextContent());
});
});Lexical is built around several key components:
Core editor functionality for creating, configuring, and managing Lexical editor instances.
function createEditor(config?: CreateEditorArgs): LexicalEditor;
interface CreateEditorArgs {
namespace?: string;
theme?: EditorThemeClasses;
onError?: (error: Error) => void;
nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;
editorState?: EditorState;
html?: HTMLConfig;
editable?: boolean;
}
interface LexicalEditor {
setRootElement(rootElement: null | HTMLElement): void;
getEditorState(): EditorState;
setEditorState(editorState: EditorState, options?: EditorSetOptions): void;
update(updateFn: () => void, options?: EditorUpdateOptions): void;
read(readFn: () => T): T;
focus(callbackFn?: () => void): void;
blur(): void;
isEditable(): boolean;
setEditable(editable: boolean): void;
}Comprehensive node types and utilities for representing and manipulating document content.
abstract class LexicalNode {
getKey(): NodeKey;
getType(): string;
clone(): LexicalNode;
createDOM(config: EditorConfig): HTMLElement;
updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean;
exportJSON(): SerializedLexicalNode;
exportDOM(editor: LexicalEditor): DOMExportOutput;
}
class TextNode extends LexicalNode {
constructor(text: string, key?: NodeKey);
getTextContent(): string;
setTextContent(text: string): this;
hasFormat(type: TextFormatType): boolean;
toggleFormat(type: TextFormatType): this;
}
class ElementNode extends LexicalNode {
getChildren(): Array<LexicalNode>;
getChildrenSize(): number;
append(...nodesToAppend: LexicalNode[]): this;
select(anchorOffset?: number, focusOffset?: number): RangeSelection;
}Advanced selection management with Range and Node selections, providing precise control over user selection and programmatic selection manipulation.
function $getSelection(): BaseSelection | null;
function $setSelection(selection: BaseSelection): void;
function $createRangeSelection(): RangeSelection;
function $createNodeSelection(): NodeSelection;
interface RangeSelection extends BaseSelection {
anchor: Point;
focus: Point;
getTextContent(): string;
insertText(text: string): void;
insertNodes(nodes: LexicalNode[]): void;
removeText(): void;
}
interface NodeSelection extends BaseSelection {
getNodes(): LexicalNode[];
add(key: NodeKey): void;
delete(key: NodeKey): void;
clear(): void;
}Comprehensive command system for handling user interactions, keyboard events, and programmatic editor operations.
function createCommand<T>(type?: string): LexicalCommand<T>;
interface LexicalEditor {
registerCommand<P>(
command: LexicalCommand<P>,
listener: CommandListener<P>,
priority: CommandListenerPriority
): () => void;
dispatchCommand<P>(command: LexicalCommand<P>, payload: P): boolean;
}
// Built-in Commands
const FORMAT_TEXT_COMMAND: LexicalCommand<TextFormatType>;
const INSERT_PARAGRAPH_COMMAND: LexicalCommand<void>;
const DELETE_CHARACTER_COMMAND: LexicalCommand<boolean>;
const UNDO_COMMAND: LexicalCommand<void>;
const REDO_COMMAND: LexicalCommand<void>;Immutable editor state management with node state capabilities for advanced use cases.
interface EditorState {
read<T>(callbackFn: () => T): T;
clone(selection?: BaseSelection): EditorState;
toJSON(): SerializedEditorState;
}
// Node State Management
function createState<T>(config: StateValueConfig<T>): StateConfig<T>;
function $getState<T>(node: LexicalNode, stateConfig: StateConfig<T>): T;
function $setState<T>(node: LexicalNode, stateConfig: StateConfig<T>, value: T): void;Essential utility functions for DOM operations, node manipulation, and editor interaction.
function $getRoot(): RootNode;
function $getNodeByKey(key: NodeKey): LexicalNode | null;
function $createTextNode(text?: string): TextNode;
function $createParagraphNode(): ParagraphNode;
// DOM Utilities
function isLexicalEditor(editor: unknown): editor is LexicalEditor;
function getNearestEditorFromDOMNode(node: Node): LexicalEditor | null;
function $getNearestNodeFromDOMNode(node: Node): LexicalNode | null;Advanced caret positioning and navigation system for precise text manipulation and cursor movement.
interface TextPointCaret {
type: 'text-point';
node: TextNode;
offset: number;
}
interface SiblingCaret {
type: 'sibling';
node: LexicalNode;
direction: 'next' | 'previous';
}
function $getTextPointCaret(node: TextNode, offset: number): TextPointCaret;
function $getSiblingCaret(node: LexicalNode, direction: CaretDirection): SiblingCaret;
function $setSelectionFromCaretRange(range: CaretRange): void;/** Text formatting constants */
const IS_BOLD: number;
const IS_ITALIC: number;
const IS_STRIKETHROUGH: number;
const IS_UNDERLINE: number;
const IS_CODE: number;
const IS_SUBSCRIPT: number;
const IS_SUPERSCRIPT: number;
const IS_HIGHLIGHT: number;
const IS_ALL_FORMATTING: number;
/** Node state key for internal state management */
const NODE_STATE_KEY: string;
/** Mapping of text format types to numeric values */
const TEXT_TYPE_TO_FORMAT: Record<string, number>;
/** Update tags for categorizing editor updates */
const HISTORIC_TAG: string;
const HISTORY_PUSH_TAG: string;
const HISTORY_MERGE_TAG: string;
const PASTE_TAG: string;
const COLLABORATION_TAG: string;
const SKIP_COLLAB_TAG: string;
const SKIP_SCROLL_INTO_VIEW_TAG: string;
const SKIP_DOM_SELECTION_TAG: string;
const FOCUS_TAG: string;type NodeKey = string;
type TextFormatType = 'bold' | 'italic' | 'strikethrough' | 'underline' | 'code' | 'subscript' | 'superscript' | 'highlight';
type ElementFormatType = 'left' | 'center' | 'right' | 'justify' | 'start' | 'end';
type CommandListenerPriority = 0 | 1 | 2 | 3 | 4;
type UpdateTag = string;
interface EditorThemeClasses {
paragraph?: string;
text?: {
bold?: string;
italic?: string;
underline?: string;
strikethrough?: string;
underlineStrikethrough?: string;
code?: string;
highlight?: string;
subscript?: string;
superscript?: string;
};
}
interface SerializedLexicalNode {
type: string;
version: number;
}
interface SerializedEditorState {
root: SerializedRootNode;
}