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:
A unique identifier for the editor instance. Required for proper functionality, especially when multiple editors exist on the same page.
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
}
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 ,
]
Whether the editor is editable. Can be toggled dynamically using editor.setEditable().
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
JSON String
Function
Empty
const savedState = '{"root":{"children":[...]}}' ;
editorState : savedState
import { $getRoot , $createParagraphNode , $createTextNode } from 'lexical' ;
editorState : ( editor ) => {
const root = $getRoot ();
const paragraph = $createParagraphNode ();
const text = $createTextNode ( 'Hello, world!' );
paragraph . append ( text );
root . append ( paragraph );
}
// Completely empty editor (no default paragraph)
editorState : null
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
Rich Text Editor
With Initial Content
Read-Only Mode
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