Skip to main content

Why Use Lexical with React?

Lexical’s React integration provides a declarative, component-based approach to building rich text editors. The @lexical/react package offers:
  • Declarative Plugin System - Plugins are React components that hook into the editor lifecycle
  • Context-Based Architecture - Access the editor instance anywhere in your component tree
  • React Hooks - Custom hooks for common editor operations and state management
  • Server-Side Rendering - Full support for SSR and Next.js applications
  • TypeScript Support - Fully typed API for better developer experience

Core Concepts

LexicalComposer

The foundation of any Lexical React application. It creates the editor instance and provides it through React Context:
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';

const initialConfig = {
  namespace: 'MyEditor',
  theme: {},
  onError: (error) => console.error(error),
};

function Editor() {
  return (
    <LexicalComposer initialConfig={initialConfig}>
      <PlainTextPlugin
        contentEditable={<ContentEditable />}
        placeholder={<div>Enter text...</div>}
        ErrorBoundary={LexicalErrorBoundary}
      />
    </LexicalComposer>
  );
}

Plugins as Components

Plugins are React components that return null but register listeners and commands during their lifecycle:
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';

function Editor() {
  return (
    <LexicalComposer initialConfig={initialConfig}>
      <PlainTextPlugin
        contentEditable={<ContentEditable />}
        placeholder={<div>Enter text...</div>}
        ErrorBoundary={LexicalErrorBoundary}
      />
      <HistoryPlugin />
      <AutoFocusPlugin />
    </LexicalComposer>
  );
}

Context-Based Access

Use useLexicalComposerContext() to access the editor instance from any child component:
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';

function CustomButton() {
  const [editor] = useLexicalComposerContext();
  
  const handleClick = () => {
    editor.update(() => {
      // Make changes to the editor state
    });
  };
  
  return <button onClick={handleClick}>Custom Action</button>;
}

Installation

npm install lexical @lexical/react

Quick Start Examples

import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';

const editorConfig = {
  namespace: 'PlainTextEditor',
  onError: (error) => console.error(error),
};

export default function PlainTextEditor() {
  return (
    <LexicalComposer initialConfig={editorConfig}>
      <PlainTextPlugin
        contentEditable={<ContentEditable className="editor" />}
        placeholder={<div>Enter some plain text...</div>}
        ErrorBoundary={LexicalErrorBoundary}
      />
      <HistoryPlugin />
    </LexicalComposer>
  );
}

Architecture Highlights

Double-Buffering Updates

Lexical uses a double-buffering system for updates:
  1. Current EditorState is cloned as work-in-progress
  2. Mutations modify the work-in-progress state
  3. Multiple synchronous updates are batched
  4. DOM reconciler diffs and applies changes
  5. New immutable EditorState becomes current

Plugin Lifecycle

Plugins follow React’s lifecycle:
  • Mount - Register commands, listeners, and transforms
  • Update - React to prop changes
  • Unmount - Cleanup functions are called automatically
function MyPlugin() {
  const [editor] = useLexicalComposerContext();
  
  useEffect(() => {
    // Register listener on mount
    return editor.registerUpdateListener(({ editorState }) => {
      // React to updates
    });
    // Cleanup on unmount
  }, [editor]);
  
  return null;
}

Best Practices

Use Hooks

Prefer built-in hooks like useLexicalComposerContext(), useLexicalEditable(), and useLexicalNodeSelection() over manual state management.

Plugin Isolation

Keep plugins focused on a single responsibility. Complex features should be broken into multiple plugins.

Context Access

Use context for accessing the editor instance rather than prop drilling.

Cleanup

Always return cleanup functions from useEffect hooks when registering listeners.

Next Steps

LexicalComposer

Learn about configuring the editor and managing initial state

Plugins

Explore the built-in plugin system and available plugins

React Hooks

Discover hooks for accessing editor state and functionality

Core Concepts

Understand Lexical’s core architecture and concepts

Build docs developers (and LLMs) love