React rich text editor.
Add WYSIWYG editing to your React app. Start with the component, or build your own toolbar with the hook.
Details
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.Get your first React editor working
- Run the sample.
Download the React starter, extract it, then run
npm ciandnpm run dev. - Check your content.
Type or paste in the editor and watch the HTML output update. This starter does not save content.
- Connect your app.
Read current HTML at submit, then add your own save API. Set up image uploads separately. Troubleshoot image uploads.
The starter uses the npm React binding shown below. For the full trial package and its browser-local save example, follow the React trial guide. Get integration help with your React version and a minimal example.
Cursor moves or edits disappear? Troubleshoot React editor state and remounting.
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
In npm version 2.16.0, TypeScript projects that check dependency declarations may encounter an onError type conflict. Updated declarations are being prepared; contact support if this blocks your integration.
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} valueFormat="html"
onChange={(next) => { if (typeof next === "string") setHtml(next); }}
config={{ height: 400 }} />;
}Option 2 — bring your own UI with the hook
The hook works in JavaScript. In npm version 2.16.0 its TypeScript declarations are incomplete; TypeScript projects can use the component above until updated declarations are published.
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
type="button" disabled={!editor.editor} aria-pressed={!!editor.state.bold}
onPointerDown={(e) => e.preventDefault()}
onClick={() => editor.run("bold")}
className={editor.state.bold ? "is-active" : ""}
>Bold</button>
<button type="button" disabled={!editor.editor || !editor.state.canUndo}
onPointerDown={(e) => e.preventDefault()}
onClick={() => 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.Save & load demoStore the HTML and read it back, with server examples that sanitize it.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.
Guides for React, JavaScript: uploads, gallery and files
Tested server code for each step, one problem per page.
- Convert rich text to plain text and download a TXT file in JavaScript
- Multiple rich text editors on one page: independent fields and form submission
- Clear and reset a rich text editor in JavaScript
- Rich text word and character count: emoji, CJK and submit limits
- Display rich text HTML outside the editor with responsive content styles
- Read-only rich text editor: view content and switch back to editing
- Rich text editor autosave: save to your server without losing edits
- Build a Markdown writing app with local drafts and downloads
- Word and Google Docs to Markdown: paste, export and check
- Upload images from a rich text editor in Node.js
- Add an image gallery to a rich text editor in Node.js
- Gallery folders in Node.js: browse and create safely
- Per-user image folders in Node.js
- Secure image uploads in Node.js
- Image upload size limits in Node.js
- Fix image upload in a React rich text editor
- React rich text editor quick start: run the trial and read form HTML
- Add an image gallery to RichTextEditor in React
Using something else? There are matching guides for Next.js, Vue, Angular, Svelte, Blazor, ASP.NET Core, PHP, and plain JavaScript.