Live Docs

Advanced /

beta

Rich Text Editor

WYSIWYG editor berbasis engine headless (TipTap) dengan toolbar & styling custom Naraya. Format teks, heading, list, quote, code, link, gambar (upload/drag/paste), undo/redo.

import { RichTextEditor } from "@naraya/ui/rich-text-editor";

Overview

Komponen ini di-expose lewat subpath @naraya/ui/rich-text-editor (bukan barrel @naraya/ui) supaya engine TipTap yang berat hanya ter-bundle saat editor benar-benar dipakai. Output berupa HTML string via onChange.

Examples

Editor + HTML output

Lihat HTML
<h2>Selamat datang 👋</h2><p>Ini editor <strong>rich text</strong> Naraya — coba <em>format</em> teks, bikin <u>list</u>, atau tambah <a href="https://naraya.ai">tautan</a>.</p><ul><li>Engine headless (TipTap)</li><li>UI 100% token Naraya</li></ul><pre><code class="language-javascript">const naraya = "design system";
console.log(`Halo ${naraya}`); // syntax highlight</code></pre>
const [html, setHtml] = useState("");

<RichTextEditor value={html} onChange={setHtml} />

Gambar (toolbar / drag / paste)

Klik gambar → kontrol align (kiri/tengah/kanan), toggle inline/wrap (teks ngalir di samping), + handle resize di pojok. Drag gambar buat pindah baris. Demo pakai fallback base64.

// Production: kasih handler upload yang balikin URL
<RichTextEditor
  value={html}
  onChange={setHtml}
  onUploadImage={async (file) => {
    const form = new FormData();
    form.append("file", file);
    const res = await fetch("/api/upload", { method: "POST", body: form });
    const { url } = await res.json();
    return url; // <- URL yang di-embed
  }}
/>

// Tanpa onUploadImage → gambar di-embed sebagai base64 data URL.

Read-only (tanpa toolbar)

<RichTextEditor value={savedHtml} editable={false} />

Tanpa toolbar (compact)

<RichTextEditor toolbar={false} minHeight="5rem" />

Props

PropTypeDefaultDescription
valuestring—HTML terkontrol. Pasangkan dengan onChange.
defaultValuestring—HTML awal (uncontrolled).
onChange(html: string) => void—Callback tiap konten berubah (HTML string).
placeholderstring"Tulis sesuatu..."Teks placeholder saat kosong.
editablebooleantruefalse = mode read-only.
toolbarbooleantrueTampilkan toolbar format.
onUploadImage(file: File) => Promise<string>—Handler upload gambar (toolbar/drag/paste) → kembalikan URL. Tanpa ini, gambar di-embed sebagai base64.
minHeightstring | number"12rem"Tinggi minimum area edit.

Roadmap

  • ✓ Selesai: format teks (bold/italic/underline/strike/code), H1-H3, list, quote, divider, link, undo/redo.
  • ✓ Gambar: upload (onUploadImage) / drag / paste, fallback base64, drag-resize + align kiri/tengah/kanan (float text-wrap).
  • ✓ Code block dengan syntax highlight (lowlight, theme-aware).
  • Phase 2 sisa: embed video (Youtube — di-skip dulu), tabel.
  • Phase 3: math/LaTeX, character count, bubble/slash menu, renderer read-only ringan (RichTextContent).

Tokens Used

--bg-surface

Latar editor

--border-subtle

Border + pemisah toolbar

--color-primary

Tool aktif, link, focus ring

--color-secondary

Latar toolbar & inline code

--text-main / --text-muted

Teks konten vs placeholder/quote

Accessibility

  • Toolbar memakai role='toolbar'; tiap tombol punya aria-label, title, dan aria-pressed untuk status aktif.
  • Area edit fokusabel dengan focus ring pada container (focus-within).
  • onMouseDown preventDefault pada tombol agar seleksi teks tidak hilang saat klik format.