Skip to main content

Overview

LexicalComposer is the root component that creates and configures a Lexical editor instance. It provides the editor through React Context, making it accessible to all child components via hooks.

Basic Usage

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>Start typing...</div>}
        ErrorBoundary={LexicalErrorBoundary}
      />
    </LexicalComposer>
  );
}

Configuration

InitialConfigType

The initialConfig prop accepts an object with the following properties:
namespace
string
required
A unique identifier for the editor instance. Required for proper functionality, especially when multiple editors exist on the same page.
namespace: 'MyEditor'
onError
(error: Error, editor: LexicalEditor) => void
required
Error handler called when the editor encounters an error during updates or reconciliation.
onError: (error, editor) => {
  console.error('Editor error:', error);
  // Optional: report to error tracking service
}
theme
EditorThemeClasses
CSS class names for styling editor elements. Each node type can have associated classes.
theme: {
  paragraph: 'editor-paragraph',
  text: {
    bold: 'editor-text-bold',
    italic: 'editor-text-italic',
    underline: 'editor-text-underline',
  },
  list: {
    ol: 'editor-list-ol',
    ul: 'editor-list-ul',
    listitem: 'editor-listitem',
  },
}
nodes
Array<Klass<LexicalNode> | LexicalNodeReplacement>
Custom node types to register with the editor. Include all custom nodes and node replacements.
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { ListNode, ListItemNode } from '@lexical/list';
import { LinkNode } from '@lexical/link';

nodes: [
  HeadingNode,
  QuoteNode,
  ListNode,
  ListItemNode,
  LinkNode,
]
editable
boolean
default:"true"
Whether the editor is editable. Can be toggled dynamically using editor.setEditable().
editable: true
editorState
InitialEditorStateType
Initial content for the editor. Accepts multiple formats:
  • null - No initialization (empty editor)
  • undefined - Default initialization (single empty paragraph)
  • string - Serialized EditorState JSON
  • EditorState - EditorState instance
  • (editor: LexicalEditor) => void - Function to programmatically set initial content
const savedState = '{"root":{"children":[...]}}';

editorState: savedState
html
HTMLConfig
Configuration for HTML import/export behavior. Customize how nodes are converted to/from HTML.
import { ParagraphNode, TextNode } from 'lexical';

html: {
  export: new Map([
    [ParagraphNode, customParagraphExport],
    [TextNode, customTextExport],
  ]),
  import: {
    p: () => ({
      conversion: customParagraphImport,
      priority: 0,
    }),
  },
}

Complete Example

import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { ListNode, ListItemNode } from '@lexical/list';
import { LinkNode } from '@lexical/link';

const theme = {
  paragraph: 'mb-2',
  text: {
    bold: 'font-bold',
    italic: 'italic',
    underline: 'underline',
  },
  heading: {
    h1: 'text-4xl font-bold mb-4',
    h2: 'text-3xl font-bold mb-3',
    h3: 'text-2xl font-bold mb-2',
  },
  list: {
    ol: 'list-decimal ml-4',
    ul: 'list-disc ml-4',
  },
  link: 'text-blue-600 hover:underline',
};

const editorConfig = {
  namespace: 'RichTextEditor',
  theme,
  nodes: [
    HeadingNode,
    QuoteNode,
    ListNode,
    ListItemNode,
    LinkNode,
  ],
  onError: (error) => {
    console.error(error);
  },
};

export default function Editor() {
  return (
    <LexicalComposer initialConfig={editorConfig}>
      <div className="editor-container">
        <RichTextPlugin
          contentEditable={
            <ContentEditable 
              className="editor-input"
              aria-placeholder="Enter text..."
              placeholder={
                <div className="editor-placeholder">
                  Enter text...
                </div>
              }
            />
          }
          ErrorBoundary={LexicalErrorBoundary}
        />
        <HistoryPlugin />
        <AutoFocusPlugin />
      </div>
    </LexicalComposer>
  );
}

Accessing the Editor Instance

Use the useLexicalComposerContext() hook to access the editor instance from child components:
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $getRoot } from 'lexical';

function EditorStats() {
  const [editor] = useLexicalComposerContext();
  const [wordCount, setWordCount] = useState(0);

  useEffect(() => {
    return editor.registerUpdateListener(({ editorState }) => {
      editorState.read(() => {
        const root = $getRoot();
        const text = root.getTextContent();
        setWordCount(text.split(/\s+/).filter(Boolean).length);
      });
    });
  }, [editor]);

  return <div>Words: {wordCount}</div>;
}

Type Signature

export type InitialEditorStateType =
  | null
  | string
  | EditorState
  | ((editor: LexicalEditor) => void);

export type InitialConfigType = Readonly<{
  namespace: string;
  nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>;
  onError: (error: Error, editor: LexicalEditor) => void;
  editable?: boolean;
  theme?: EditorThemeClasses;
  editorState?: InitialEditorStateType;
  html?: HTMLConfig;
}>;

export function LexicalComposer({
  initialConfig,
  children,
}: {
  initialConfig: InitialConfigType;
  children: React.ReactNode;
}): JSX.Element;

Important Notes

The initialConfig prop is only used during the initial mount. Changes to this prop after mount will not affect the editor. To dynamically update the editor configuration, use the editor instance methods.
The editor instance is created using useMemo with an empty dependency array, ensuring it’s only created once during the component lifecycle.
For server-side rendering (SSR), the editor gracefully handles the absence of DOM APIs. The actual editor initialization occurs on the client side.

LexicalComposerContext

Access the editor instance from child components

Plugins Overview

Learn about available plugins and how to create custom ones

Editor Instance

Explore editor instance methods and properties

Theme Configuration

Deep dive into theme configuration and styling

Build docs developers (and LLMs) love