Skip to content

Repository files navigation

Aksara React Editor

Reusable React + TypeScript rich text editor, powered by Tiptap/ProseMirror, KaTeX, and DOMPurify. Includes an interactive local playground and an installable ESM package with TypeScript declarations.

Release: 0.1.0. This is an initial library release, not full parity with every RichTextEditor.com demo, and not an independently security-audited product.

Run the demo

Requires Node.js 22.12+ and npm.

npm ci
npm run dev

Open the local address printed by Vite. The demo is in-memory: changes disappear on reload or when switching examples. It makes no persistence or AI requests. External images, if inserted, contact their hosts.

Install in another app

# In this library directory:
npm run build
npm pack

# In your React app (replace the path):
npm install @codeverta/rich
"use client"; // For a Next.js client component
import { useRef } from "react";
import {
  RichTextEditor,
  type RichTextEditorHandle,
  type EditorValue,
} from "@codeverta/rich";
import "@codeverta/rich/style.css";
import "katex/dist/katex.min.css";

export function DocumentEditor() {
  const editor = useRef<RichTextEditorHandle>(null);
  function handleChange(value: EditorValue) {
    // value.html: sanitized HTML
    // value.json: editor document (validate again at the server)
    // value.text: plain text
    console.log(value.text.length);
  }
  return (
    <RichTextEditor
      ref={editor}
      initialHTML="<h1>My document</h1><p>Start writing…</p>"
      onChange={handleChange}
      onError={(error) => console.error(error.message)}
      label="Article editor"
    />
  );
}

React and React DOM are peer dependencies; tested here with React 19. React 18 compatibility is declared but has not been separately tested. Import both CSS files. No global CSS reset is included in the library. The demo CSS is separate. In server-rendered frameworks, use a client component; pure conversion utilities require a browser DOM. The editor defers initialization until the client.

Feature matrix

Capability Status
Paragraphs; headings 1–6; bold, italic, underline, strike Implemented
Font family, size, color, highlight Implemented
Subscript, superscript, inline code, clear formatting Implemented
Left, center, right, justified alignment Implemented
Ordered, unordered, nested task lists Implemented; list nesting uses standard editor keyboard shortcuts
Blockquotes, code blocks, dividers, hard breaks Implemented
Undo/redo and keyboard shortcuts Implemented
Table insertion, column resizing, add/delete rows/columns Implemented
Merge/split cells, toggle header, delete table Implemented; select adjacent cells before merge
Links, autolinks, unlink Implemented; explicit permitted URL schemes
Images HTTPS URL insertion and alternative text; optional upload callback for PNG, JPEG, GIF, and WebP files from picker, paste, or drop
Math Inline/block LaTeX; KaTeX preview; presets; click equation to edit
HTML source editor, paste sanitization Implemented
HTML, JSON, plain-text import/export Implemented; JSON schema is specific to this version
Markdown import/export Basic, lossy conversion; styling, task state and table structure may be lost; math source exported as dollar-delimited LaTeX, imported as text
Find/replace Case-insensitive literal matching across inline formatting within each block; no regex or cross-block matching
Word/character count, fullscreen, read-only Implemented
Multiple editor instances Implemented, isolated state
Print/PDF Browser print dialog; host app controls final pagination
Image storage, asset manager, video/audio embeds Host app must provide an image upload endpoint and storage; asset manager and video/audio embeds are not implemented
Real-time collaboration, presence, comments, tracked changes, version history Not implemented
AI, translation, voice dictation, mentions Not implemented
Word/Excel import fidelity, DOCX export, email-safe HTML Not implemented
Paginated layout, templates, advanced image editing Not implemented
Accessibility certification, mobile/IME/browser compatibility matrix Not audited; native buttons, labels, dialogs, and editor keyboard behavior provided

Component API

Prop Type Behavior
initialHTML string Sanitized initial content. Changing this prop does not replace the document; use the ref or remount.
onChange (value: EditorValue) => void Called on document edits; receives HTML, JSON, and text. Debounce persistence in the host app.
onError (error: Error) => void Reports UI import/paste/command failures. Invalid initial HTML can throw during initialization; use an error boundary.
onImageUpload (file: File) => Promise<string> Optional. Upload one validated PNG/JPEG/GIF/WebP file and return its full HTTPS URL. Enables picker, paste, and drop uploads.
readOnly boolean Disables document modification through the component and its ref.
placeholder string Initial placeholder configuration.
label string Accessible editor label.
className string Class on the editor root.

Ref methods: getValue(): EditorValue | null, setHTML(html): void, setJSON(json: unknown): void, focus(): void, clear(): void. Getters return null before initialization. Setters may throw for invalid input or read-only mode. Handle these errors in the host app. No raw editor instance is exposed, to keep the public input boundary explicit.

Image upload handler

Provide your application's authenticated upload endpoint through onImageUpload. The editor checks declared type, extension, file signature, and a 5 MB size ceiling before calling it. A successful callback must return a full HTTPS image URL; rejected promises and unsafe URLs appear in onError and the editor UI. Without the callback, file paste/drop is rejected and image insertion by HTTPS URL remains available.

async function uploadImage(file: File): Promise<string> {
  const body = new FormData();
  body.append("image", file);
  const response = await fetch("/api/images", {
    method: "POST",
    body,
    credentials: "same-origin",
  });
  if (!response.ok) throw new Error("Image upload failed.");
  const result: unknown = await response.json();
  if (
    !result ||
    typeof result !== "object" ||
    !("url" in result) ||
    typeof result.url !== "string"
  )
    throw new Error("Image upload returned an invalid response.");
  return result.url;
}

<RichTextEditor onImageUpload={uploadImage} onError={console.error} />;

The host endpoint must authenticate and authorize the upload, check the decoded content and dimensions, re-encode or otherwise safely process images, enforce storage limits, and return a URL served over HTTPS. Client checks can be bypassed.

Exports: sanitizeHTML, safeURL, jsonToHTML, markdownToHTML, htmlToMarkdown, MAX_HTML_LENGTH, MAX_LATEX_LENGTH, and the component/types.

Content and security

See SECURITY.md for the complete trust boundary. HTML export is an editor fragment, not a complete web page. Math HTML stores LaTeX source in data-latex; reopening it through this editor renders equations. Render exported fragments in another product only after your own output sanitization. JSON is an interchange format, not a reason to trust stored content.

HTML/text file input is limited to 1 MB; direct HTML input to 1,000,000 JavaScript characters. Editor typing is limited to 200,000 characters, and equation dialog input to 4,096 characters. JSON is additionally limited to 20,000 nodes/marks and depth 64. These bounds reduce accidental resource exhaustion; they do not guarantee CPU/memory limits for every adversarial document. Imported documents over the typing character limit may open but need shortening before further insertion.

Validate and package

npm run check       # regression tests, TypeScript, library and demo builds
npm audit          # known dependency advisories at the time of execution
npm pack           # installable package

Tests exercise malicious HTML and URLs, CSS filtering, DOM clobbering, JSON limits, math trust, formatting, undo/redo, tables, read-only mode, independent instances, equation/image dialogs, and image upload validation and failures. Tests run in jsdom, not a full browser engine. No penetration test or cross-browser audit is implied.

Source is under src/; demo under demo/; tests under tests/. Library build output is dist/, demo build output is demo-dist/. The package does not contain a backend and is not published to npm.

References

The supplied code is MIT licensed. Third-party dependencies retain their own licenses.

rich

rich

About

Our Company Rich Text Editor

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages