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 Tiptap-style with the useRichTextEditor hook. Same engine either way: AI Toolkit, per-node CRDT collaboration, tracked changes, and clean HTML output.

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

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.