React editor

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.

React focus

Built to respect React’s ownership model.

Component or hookControlled value/onChange component, or a headless hook whose state re-renders your own buttons on every selection change.
Typed imperative handleRichTextEditorReactHandle exposes getHTMLCode, getJSON/setJSON, focus, statistics, outline, and the plugin surfaces.
StrictMode & SSR safeMount/unmount is idempotent and teardown calls editor.destroy(), so React 18 StrictMode double-invoke and App Router client components just work.
Sony
Intel
Nokia
Siemens
IBM
Microsoft

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} />;
}

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.