A few months ago, I was working on a React project for a client in New York who needed a custom text editor for internal documentation. They wanted something that felt like Google Docs, clean, fast, and with formatting options like bold, italic, and bullet lists.
At first glance, it seemed like a simple task. But once I started exploring, I realized there were several great ways to build a React text editor component, each with its own strengths.
In this tutorial, I’ll walk you through two methods I’ve personally used to create a text editor in React, one using Lexical (by Meta) and another using CKEditor 5. Both are powerful, modern solutions, and I’ll share complete working examples for each so you can easily integrate them into your own projects.
What Is a React Text Editor Component?
A text editor component in React allows users to create and format text directly inside your web app. It’s commonly used in dashboards, CMS systems, blogs, and note‑taking tools.
Unlike a simple <textarea>, a rich text editor supports formatting like bold, italic, underline, lists, links, and images, all while maintaining data consistency through React’s state management.
Method 1 – Build a React Text Editor Using Lexical
Lexical is a lightweight, extensible text editor framework developed by Meta (Facebook). It’s highly modular and gives you full control over formatting, keyboard shortcuts, and plugins.
When I first used Lexical, I was impressed by how fast and customizable it was compared to older editors like Draft.js.
Step 1 – Install Lexical
Open your terminal and run:
npm install lexical @lexical/reactThis installs the Lexical core and its React bindings.
Step 2 – Create the Editor Component
Here’s the complete code for a simple Lexical text editor:
// File: LexicalEditor.js
import React from "react";
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 { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
function EditorChangeHandler() {
const [editor] = useLexicalComposerContext();
React.useEffect(() => {
return editor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
const textContent = editor.getEditorState().toJSON();
console.log("Editor content:", textContent);
});
});
}, [editor]);
return null;
}
export default function LexicalEditor() {
const config = {
namespace: "MyEditor",
theme: {
paragraph: "editor-paragraph",
},
onError(error) {
console.error(error);
},
};
return (
<LexicalComposer initialConfig={config}>
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<div className="editor-placeholder">Start typing...</div>}
/>
<HistoryPlugin />
<OnChangePlugin onChange={(editorState) => console.log(editorState)} />
<EditorChangeHandler />
</LexicalComposer>
);
}Step 3 – Add Some Basic Styles
/* File: LexicalEditor.css */
.editor-input {
min-height: 200px;
border: 1px solid #ccc;
padding: 10px;
border-radius: 6px;
font-size: 16px;
outline: none;
}
.editor-placeholder {
color: #aaa;
position: absolute;
pointer-events: none;
}Step 4 – Use It in Your App
// File: App.js
import React from "react";
import LexicalEditor from "./LexicalEditor";
import "./LexicalEditor.css";
function App() {
return (
<div style={{ width: "70%", margin: "40px auto" }}>
<h2>React Text Editor with Lexical</h2>
<LexicalEditor />
</div>
);
}
export default App;You can see the output in the screenshot below.

This setup creates a fully functional text editor that supports basic formatting and undo/redo history. The OnChangePlugin listens for changes, allowing you to save or sync content in real time.
Lexical is ideal when you need a lightweight, customizable editor that doesn’t rely on heavy dependencies.
Method 2 – Use CKEditor 5 with React
When clients need a more feature‑rich editor with built‑in toolbar options, image uploads, and collaboration tools, I often go with CKEditor 5.
It’s a battle‑tested WYSIWYG editor with an official React integration, making it easy to use with minimal setup.
Step 1 – Install CKEditor
Run the following command:
npm install @ckeditor/ckeditor5-react @ckeditor/ckeditor5-build-classicStep 2 – Create the Editor Component
Here’s the complete working code:
// File: CKEditorComponent.js
import React, { useState } from "react";
import { CKEditor } from "@ckeditor/ckeditor5-react";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
export default function CKEditorComponent() {
const [content, setContent] = useState("");
return (
<div style={{ width: "70%", margin: "40px auto" }}>
<h2>React Text Editor with CKEditor 5</h2>
<CKEditor
editor={ClassicEditor}
data="<p>Hello from CKEditor 5!</p>"
onReady={(editor) => {
console.log("Editor is ready to use!", editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
setContent(data);
}}
/>
<h4>Editor Output:</h4>
<div
style={{
background: "#f9f9f9",
border: "1px solid #ddd",
padding: "10px",
borderRadius: "6px",
}}
dangerouslySetInnerHTML={{ __html: content }}
/>
</div>
);
}This code creates a classic CKEditor instance inside a React component. The onChange event keeps track of the content in real time, which you can save to a database or display elsewhere in your app.
Step 3 – Add Toolbar Customization (Optional)
You can customize the toolbar of CKEditor to include only the tools you need.
const editorConfiguration = {
toolbar: ["bold", "italic", "link", "bulletedList", "numberedList", "undo", "redo"],
};Then, pass it as a prop:
<CKEditor
editor={ClassicEditor}
config={editorConfiguration}
data="<p>Start writing...</p>"
onChange={(event, editor) => setContent(editor.getData())}
/>You can see the output in the screenshot below.

This gives users a clean, focused editing experience.
Method 3 – Build a Minimal Text Editor with contentEditable
If you don’t want to rely on third‑party libraries, you can build a minimal custom editor using plain React and the contentEditable attribute.
Here’s a simple example I often use for prototypes:
// File: SimpleEditor.js
import React, { useState } from "react";
export default function SimpleEditor() {
const [content, setContent] = useState("Type something here...");
return (
<div style={{ width: "70%", margin: "40px auto" }}>
<h2>Simple React Text Editor</h2>
<div
contentEditable
suppressContentEditableWarning
style={{
border: "1px solid #ccc",
padding: "10px",
minHeight: "150px",
borderRadius: "6px",
}}
onInput={(e) => setContent(e.currentTarget.innerHTML)}
dangerouslySetInnerHTML={{ __html: content }}
/>
<h4>Editor Output:</h4>
<div
style={{
background: "#f0f0f0",
padding: "10px",
borderRadius: "6px",
}}
dangerouslySetInnerHTML={{ __html: content }}
/>
</div>
);
}This approach uses native browser capabilities. It’s lightweight and great for quick demos, but it lacks advanced formatting controls and consistent cross‑browser behavior.
Choosing the Right Method
| Method | Best For | Key Features |
|---|---|---|
| Lexical | Developers who want flexibility and performance | Modern, modular, lightweight |
| CKEditor 5 | Teams that need a full‑featured editor | Toolbar, image upload, and collaboration |
| contentEditable | Quick prototypes or minimal apps | Simple and dependency‑free |
From experience, I usually start with Lexical for internal tools and CKEditor for production‑grade web apps.
Bonus Tip – Saving Editor Data
No matter which method you choose, you’ll likely need to save the editor’s content to a server or local storage.
Here’s a quick example using localStorage:
useEffect(() => {
localStorage.setItem("editorContent", content);
}, [content]);This ensures your users never lose their work if they refresh the page.
Building a React text editor component can seem intimidating at first, but once you understand the available tools, it’s actually quite straightforward.
Lexical gives you a developer‑friendly, modular foundation, while CKEditor offers a polished, enterprise‑ready experience. And if you just need something lightweight, React’s native contentEditable can do the job.
You may also read:
- React Function Components with TypeScript
- How to Use React Frame Component
- Get Fetch Results in a React Functional Component
- How to Use Card Component in React JS?

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.