DEV Community

Build Full-Featured Rich Text Editors in React ( Lexical + Lexkit )

In this article, I’ll share my step-by-step approach to building a full-featured Rich Text Editor in React.

The reason I chose Lexical (over Tiptap or others) is simple: speed ⚡.

Lexical: Rich Text Editor built by meta

But… Lexical by itself can be a bit painful to work with.

It’s low-level, verbose, and you often end up handling too much boilerplate.

That’s why I built LexKit — an open-source layer on top of Lexical.

It’s DX-friendly, type-safe, and ships with ready-to-use templates (even for shadcn).

👉 It gives us the full power of Lexical, but with a lot more out-of-the-box features.

👉 Plug-and-play extensions.

👉 Strong type safety.

Lexkit: Rich text editor over Lexical

📚 Great docs are here → lexkit.dev/docs


🛠️ Installing LexKit

First, install LexKit + Lexical in your project:

npm install @lexkit/editor
Enter fullscreen mode Exit fullscreen mode

And install the required Lexical packages:

npm install lexical @lexical/react @lexical/html @lexical/markdown @lexical/list @lexical/rich-text @lexical/selection @lexical/utils
Enter fullscreen mode Exit fullscreen mode

✨ Creating the Basic Editor

Let’s create a basic editor together:

import {
  createEditorSystem,
  boldExtension,
  italicExtension,
  historyExtension,
  listExtension,
  linkExtension,
  RichText,
} from "@lexkit/editor";
import "./basic-editor.css";

// 1. Define your extensions (as const for type safety)
const extensions = [
  boldExtension,
  italicExtension,
  listExtension,
  linkExtension,
  historyExtension,
] as const;

// 2. Create typed editor system
const { Provider, useEditor } = createEditorSystem<typeof extensions>();
Enter fullscreen mode Exit fullscreen mode

The nice thing?
useEditor is fully type-safe — it knows exactly what commands you have.


🛠️ Toolbar Example

Here’s a simple toolbar with some text formatting actions:

// Toolbar Component - Shows basic text formatting buttons
function Toolbar() {
  const { commands, activeStates } = useEditor();

  return (
    <div className="basic-toolbar">
      <button
        onClick={() => commands.toggleBold()}
        className={activeStates.bold ? "active" : ""}
        title="Bold (Ctrl+B)"
      >
        Bold
      </button>
      <button
        onClick={() => commands.toggleItalic()}
        className={activeStates.italic ? "active" : ""}
        title="Italic (Ctrl+I)"
      >
        Italic
      </button>
      <button
        onClick={() => commands.toggleUnorderedList()}
        className={activeStates.unorderedList ? "active" : ""}
        title="Bullet List"
      >
        • List
      </button>
      <button
        onClick={() => commands.toggleOrderedList()}
        className={activeStates.orderedList ? "active" : ""}
        title="Numbered List"
      >
        1. List
      </button>
      <button
        onClick={() => commands.undo()}
        disabled={!activeStates.canUndo}
        className={!activeStates.canUndo ? "disabled" : ""}
        title="Undo (Ctrl+Z)"
      >
        ↶ Undo
      </button>
      <button
        onClick={() => commands.redo()}
        disabled={!activeStates.canRedo}
        className={!activeStates.canRedo ? "disabled" : ""}
        title="Redo (Ctrl+Y)"
      >
        ↷ Redo
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

📝 Putting It All Together

Now, let’s use our Toolbar inside the editor provider:

// Main Component
export function BasicEditorExample() {
  return (
    <Provider extensions={extensions}>
      <div className="basic-editor">
        <Toolbar />
        <RichText
          classNames={{
            container: "basic-editor-container",
            contentEditable: "basic-content",
            placeholder: "basic-placeholder",
          }}
          placeholder="Start writing your content here..."
        />
      </div>
    </Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

To style it, just copy this CSS into your project:
👉 basic-editor.css

🎉 Boom — you’ve got your first editor running!

Try it out live in the Playground.


📦 Templates

The same way we created the basic editor, you can import more extensions or use prebuilt templates:


📚 Docs & Demos

Everything is documented here:


💡 With LexKit, you can build Notion-like editors, blog editors, CMS inputs, or any custom RTE — all while staying type-safe and React-friendly.

If you’re into React + TypeScript, I think you’ll love it ❤️
I'd love to hear your thoughts. Feel free to drop a comment <3

Top comments (0)