Next.js editor

A rich text editor built to fit the Next.js App Router.

Run the editor in a client component, render saved content on the server without a DOM, and skip the hydration pitfalls. This documentation site is itself a Next.js app running the editor on every demo page, so the guidance here is what we ship.

Next.js focus

App Router, SSR, and RSC — handled.

Client component, server dataThe editor is a "use client" surface; your Server Components fetch the content and pass it down as a prop.
Render saved content on the serverThe server entry turns the structured JSON document into HTML with no browser — perfect for the published/read view.
Hydration-safeMount/unmount is idempotent and calls editor.destroy() on cleanup, so React 18 StrictMode’s double-invoke does not leak.
Sony
Intel
Nokia
Siemens
IBM
Microsoft

Install the package

One npm package ships the engine, the React bindings, and the plugin bundle. React 18+ is a peer dependency, which the Next.js App Router already provides.

npm install @richscripts/richtexteditor

1. Wrap the editor in a client component

The editor mounts into a live DOM and manages its own toolbars and iframe, so it belongs in a component marked "use client". Keep the component small — it just renders the editor and takes the initial content as a prop.

"use client";
// The editor renders into a live DOM, so it must run in a client component.
// Server components pass the initial HTML down as a prop.
import { RichTextEditor } from "@richscripts/richtexteditor/react";

export default function EditorClient({ initialHtml }: { initialHtml: string }) {
  return <RichTextEditor defaultValue={initialHtml} config={{ height: 480 }} />;
}

2. Fetch content in a Server Component, pass it down

Your route stays a Server Component: it loads the document with your data layer and hands the HTML to the client editor. No editor code runs on the server for the editing view.

// app/edit/[id]/page.tsx  — a Server Component
import EditorClient from "./EditorClient";

export default async function EditPage({ params }: { params: { id: string } }) {
  const doc = await loadDocument(params.id);   // your data layer
  return <EditorClient initialHtml={doc.html} />;
}

3. Render saved content on the server for the read view

For the published page — where visitors read the content but do not edit — you do not need the editor at all. The package’s server entry converts the structured JSON document to an HTML string with no DOM, so it works in a Server Component or a route handler.

// app/post/[slug]/page.tsx — render SAVED content on the server, no DOM needed
import { renderHTML } from "@richscripts/richtexteditor/server";

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await loadPost(params.slug);     // { json: structured document }
  const html = renderHTML(post.json);           // structured JSON -> HTML string
  return <article dangerouslySetInnerHTML={{ __html: html }} />;
}

The re-render gotcha (and why the component avoids it)

RichTextEditor replaces its host element’s children with its own chrome. If you mount the raw engine onto a dangerouslySetInnerHTML host and a parent re-renders (for example setState right after mount), React re-applies the initial HTML and wipes the live editor. The supported <RichTextEditor> component handles this for you; if you build your own wrapper, memoize the host so React never re-applies its markup:

"use client";
import { memo, useEffect, useRef } from "react";
import { RichTextEditor } from "@richscripts/richtexteditor/react";

// The <RichTextEditor> component already handles this. Only relevant if you
// mount the raw engine yourself onto a dangerouslySetInnerHTML host: memoize
// the host so a parent re-render (e.g. setState after mount) cannot re-apply
// the initial HTML and wipe the live editor.
const Host = memo(function Host({ hostRef }: { hostRef: React.RefObject<HTMLDivElement | null> }) {
  return <div ref={hostRef} dangerouslySetInnerHTML={{ __html: "<p></p>" }} />;
});

Why teams pick it for Next.js products

  • 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.
  • Server rendering built in: the same structured document you edit renders to static HTML on the server for fast, SEO-friendly published pages.
  • Perpetual license from $129: no per-seat AI pricing and no subscription — see pricing and the Tiptap / Lexical comparisons.