The React rich text editor with a component and a hook.
Drop in the controlled <RichTextEditor> component for a full toolbar in one line, or build your own UI with the useRichTextEditor hook. Add content editing to your CMS, knowledge base, or business application, then save the HTML through your own API.
Paid licenses start at $129 for one domain, with perpetual use of your licensed version and one year of updates. AI provider usage, optional hosting, and future update renewals are separate.
Built to respect React’s ownership model.
value/onChange component, or a headless hook whose state re-renders your own buttons on every selection change.RichTextEditorReactHandle exposes getHTMLCode, getJSON/setJSON, focus, statistics, outline, and the plugin surfaces.editor.destroy(), so React 18 StrictMode double-invoke and App Router client components just work.Evaluate it with your application’s content
Start with the Vite + React sample app. Paste a document your users work with, check the formatting, then save and reload its HTML through your API. Test your upload workflow before choosing a deployment license. If integration blocks your evaluation, contact support with your React version and a minimal example.
Install once, pick your integration style
One npm package ships the engine, the React bindings, and the plugin bundle. React 18+ is a peer dependency.
npm install @richscripts/richtexteditor
Option 1 — the drop-in component
A controlled component with the familiar value/onChange contract. Pass any editor option through config — toolbar presets, height, upload endpoints, AI settings.
import { useState } from "react";
import { RichTextEditor } from "@richscripts/richtexteditor/react";
export function Composer() {
const [html, setHtml] = useState("<p>Draft your update...</p>");
return <RichTextEditor value={html} onChange={setHtml} config={{ height: 400 }} />;
}Option 2 — bring your own UI with the hook
useRichTextEditor returns the live editor plus a state snapshot (bold, h2, canUndo, ...) that re-renders your buttons as the selection moves — the pattern Tiptap and Lexical users expect, without giving up the built-in plugins. Use onMouseDown + preventDefaultso your buttons don’t steal focus from the editor.
import { useRichTextEditor, EditorContent } from "@richscripts/richtexteditor/react";
export function Editor() {
const editor = useRichTextEditor({ value: "<p>Hello</p>" });
return (
<>
<button
onMouseDown={(e) => { e.preventDefault(); editor.run("bold"); }}
className={editor.state.bold ? "is-active" : ""}
>Bold</button>
<button disabled={!editor.state.canUndo}
onMouseDown={(e) => { e.preventDefault(); editor.run("undo"); }}
>Undo</button>
<EditorContent editor={editor} />
</>
);
}Reading and writing content imperatively
Attach a ref to get a typed RichTextEditorReactHandle: HTML in/out, the structured JSON document model, text statistics, document outline, and accessors for AI Toolkit, comments, tracked changes, and collaboration.
import { useRef } from "react";
import { RichTextEditor } from "@richscripts/richtexteditor/react";
import type { RichTextEditorReactHandle } from "@richscripts/richtexteditor/react";
export function Form() {
const editorRef = useRef<RichTextEditorReactHandle>(null);
const submit = () => {
const html = editorRef.current?.getHTMLCode() ?? "";
const json = editorRef.current?.getJSON(); // structured document
// send html / json to your API ...
};
return (
<>
<RichTextEditor ref={editorRef} defaultValue="<p></p>" />
<button onClick={submit}>Save</button>
</>
);
}Next.js and server rendering
The editor is a client-side surface. In the App Router, wrap it in a "use client" component; this very site is a Next.js app running the editor on its demo pages. For rendering saved content on the server, the package’s server entry converts the structured JSON to static HTML without a DOM.
"use client";
// The editor renders into a live DOM, so in Next.js App Router keep it in a
// client component. Server components can pass initial HTML down as a prop.
import { RichTextEditor } from "@richscripts/richtexteditor/react";
export default function EditorClient({ initialHtml }: { initialHtml: string }) {
return <RichTextEditor defaultValue={initialHtml} />;
}getJSON/setJSON.React starter downloadA complete Vite + React sample app is in the download bundle.Why teams pick it over building on a framework-only editor
- Everything is included: AI Toolkit (bring your own key), per-node Yjs CRDT collaboration, tracked changes, comments, and revision history are part of the license — not premium add-ons.
- Clean HTML out: the output is publish-ready markup, plus a structured JSON model when you want validation or server rendering.
- Perpetual license from $129: no per-seat AI pricing and no subscription; see pricing and the Tiptap / Lexical comparisons.
Using something else? There are matching guides for Next.js, Vue, Angular, Svelte, Blazor, ASP.NET Core, PHP, and plain JavaScript.