Documentation · itzsa

Editor

TipTap rich text editor with toolbar, HTML mode, tables and media, Nepali Unicode/Preeti, language lock, and sanitized HTML/URL ingress.

@itzsa/editorTipTapUnicode / Preeti

Installation#

Peer-friendly TipTap editor. Add nepali-input when using Nepali modes.

pnpm add @itzsa/editor @itzsa/nepali-input
@source "../node_modules/@itzsa/editor";
@import "@itzsa/editor/styles.css";

Live demo#

Loading editor…

Uploads use onUpload → https URL (no base64)

Starter#

Controlled HTML plus a host-owned upload handler.

"use client";

import { useState } from "react";
import {
  RichTextEditor,
  type EditorUploadHandler,
} from "@itzsa/editor";
import "@itzsa/editor/styles.css";

const onUpload: EditorUploadHandler = async (file, { signal, onProgress }) => {
  const body = new FormData();
  body.append("file", file);
  const res = await fetch("/api/upload", { method: "POST", body, signal });
  if (!res.ok) throw new Error("Upload failed");
  onProgress({ ratio: 1 });
  const { url } = await res.json();
  return url; // https://cdn.example.com/...
};

export function Example() {
  const [html, setHtml] = useState("<p>Hello</p>");

  return (
    <RichTextEditor
      value={html}
      onChange={setHtml}
      onUpload={onUpload}
      settings={{
        nepali: "unicode",
        maxLength: 5000,
        media: { maxImageBytes: 5_000_000 },
      }}
    />
  );
}

Uploads#

Files are never inlined as base64.

Pass onUpload or settings.media.onUpload that returns a durable https:// CDN URL. URL paste still works without an uploader when allowUrlInsert is true.

settings.media

PropTypeDefaultDescription
onUploadEditorUploadHandler(file, { kind, signal, onProgress }) => Promise<httpsUrl>. Required for file pick / drag-drop.
maxImageBytesnumber5MBMax image upload size in bytes.
maxVideoBytesnumber50MBMax video upload size in bytes.
acceptImagereadonly string[]png/jpeg/gif/webpAllowed image MIME types.
acceptVideoreadonly string[]mp4/webm/oggAllowed video MIME types.
allowUrlInsertbooleantrueAllow pasting an https media URL without uploading a file.

Settings#

Prefer the settings bag for production wiring; flat props still merge.

settings={{
  nepali: "unicode",
  maxLength: 5000,
  compact: true,
  showStatusBar: true,
  allowHtmlMode: true,
  sanitize: true,
  toolbar: { video: false, html: false },
  media: {
    onUpload,
    maxImageBytes: 5_000_000,
    maxVideoBytes: 50_000_000,
    allowUrlInsert: true,
  },
  locale: { placeholder: "लेख्नुहोस्…" },
  classNames: { root: "my-editor" },
}}

EditorSettings

PropTypeDefaultDescription
placeholderstringEmpty state placeholder text.
nepali"unicode" | "preeti" | falsefalseNepali input mode for the document.
maxLengthnumberCharacter limit shown in the status bar.
minHeight / compactstring / booleanLayout density and content min-height.
showStatusBarbooleantrueShow or hide the bottom status bar.
allowHtmlModebooleantrueEnable HTML source mode in the toolbar.
sanitizebooleantrueRun HTML / URL sanitizers on ingress.
immediatelyRenderbooleanfalseTipTap SSR-related render flag.
toolbarEditorToolbarFeaturesPer-feature toolbar flags (see toolbar table).
classNamesEditorClassNamesClass name slots for chrome regions.
localePartial<EditorLocaleText>Partial locale string overrides.
extensionsExtensionsAdditional TipTap extensions.
mediaEditorMediaSettingsUpload handler, size limits, MIME accept lists, URL insert.

Security#

Sanitization on ingress — still sanitize again when rendering stored HTML.

  • HTML sanitizer on value, paste, and HTML mode
  • URL allowlist — blocks javascript:, data: (unless explicitly allowed), and protocol-relative phishing URLs
  • Safe CSS lengths only for width / font-size
  • Video via schema node — no HTML string injection
  • Prefer onUpload for production media

Rendering

Sanitize again before dangerouslySetInnerHTML. See the package README.

Security helpers

PropTypeDefaultDescription
sanitizeHtml(html: string) => stringSanitize HTML before storage or display.
sanitizeUrl / validateLinkHrefURL helpersAllowlist http(s) and relative URLs; block javascript: / data:.
sanitizeCssLength(value: string) => string | nullSafe CSS length for width / font-size attributes.
validateMediaInsertmedia guardValidate media URLs before insert.

Props API#

Full prop tables for RichTextEditor, toolbar, classNames, and the ref handle.

RichTextEditor#

Top-level component props. Flat props merge with settings.

RichTextEditorProps

PropTypeDefaultDescription
valuestring""Controlled HTML content.
onChange(html: string) => voidFires when the document HTML changes.
onBlur / onFocus() => voidFocus lifecycle callbacks.
refRef<RichTextEditorHandle>Imperative API: getHTML, getJSON, getText, setContent, clear, focus, blur, isEmpty, getEditor.
labelstringOptional visible label above the editor.
placeholderstringEmpty-document placeholder (also via settings / locale).
disabledbooleanfalseDisables editing and toolbar actions.
readOnlybooleanfalseView-only content; toolbar stays mostly inactive.
invalidbooleanfalseShows invalid / error border styling.
nepali"unicode" | "preeti" | falsefalseEnable Nepali transliteration input (requires @itzsa/nepali-input).
maxLengthnumberSoft character limit; status bar warns when reached.
minHeightstringEditor surface min-height CSS value (e.g. "220px").
compactbooleanfalseTighter toolbar and content padding.
showStatusBarbooleantrueWord / character counts and limit messaging.
allowHtmlModebooleantrueAllow switching to raw HTML editing panel.
sanitizebooleantrueSanitize HTML on value, paste, setContent, and HTML mode apply.
immediatelyRenderbooleanfalseTipTap immediatelyRender (prefer false under SSR / Next.js).
onUploadEditorUploadHandlerHost uploader for device files. Prefer settings.media.onUpload in production.
maxUploadBytesnumberDeprecated — use settings.media.maxImageBytes / maxVideoBytes.
toolbarEditorToolbarFeaturesToggle toolbar groups (history, headings, table, image, …).
classNamestringRoot class name.
classNamesEditorClassNamesSlots: root, toolbar, content, statusBar, htmlPanel.
localePartial<EditorLocaleText>Override toolbar / modal / status strings.
extensionsExtensionsExtra TipTap extensions merged into the editor schema.
settingsEditorSettingsScalable config bag. Flat props merge with / override matching settings keys.

Toolbar features#

Pass via toolbar or settings.toolbar.

EditorToolbarFeatures

PropTypeDefaultDescription
historybooleantrueUndo / redo.
headingsbooleantrueHeading level controls.
fontsbooleantrueFont family / size controls.
marksbooleantrueBold, italic, underline, strike, highlight.
colorbooleantrueText color picker.
transform / scriptbooleantrueCase transform and sub/superscript.
link / code / htmlbooleantrueLink popup, inline/block code, HTML mode toggle.
align / lists / quotebooleantrueAlignment, lists, and blockquote.
table / image / video / rulebooleantrueInsert table, image, video, horizontal rule.

Class names#

Slot class names for chrome regions.

EditorClassNames

PropTypeDefaultDescription
rootstringOuter editor chrome.
toolbarstringToolbar row.
contentstringProseMirror / EditorContent surface.
statusBarstringBottom status bar.
htmlPanelstringRaw HTML mode panel.

Ref handle#

Imperative API via ref.

RichTextEditorHandle

PropTypeDefaultDescription
getHTML()() => stringCurrent document as HTML.
getJSON()() => Record<string, unknown>TipTap JSON document.
getText()() => stringPlain text content.
setContent(html, options?)(html, { emitUpdate? }) => voidReplace content; optionally emit onChange.
clear()() => voidClear the document.
focus() / blur()() => voidFocus or blur the editor surface.
isEmpty()() => booleanWhether the document is empty.
getEditor()() => Editor | nullUnderlying TipTap instance (advanced).