npm package: @remyxjs/react
Description: React components and hooks for the Remyx rich-text editor
Author: Remyx
Latest version: 1.3.0-beta

![Remyx Editor](https://raw.githubusercontent.com/SmashJaw/remyxjs/refs/heads/master/docs/screenshots/Remyx-Logo.svg)

# @remyxjs/react

A feature-rich WYSIWYG editor for React, built on the framework-agnostic [`@remyxjs/core`](https://www.npmjs.com/package/@remyxjs/core) engine. Configurable toolbar, menu bar, markdown support, theming, file uploads, and a plugin system.

Visit us at [`SmashJaw.com`](https://smashjaw.com)
or the RemyxJS official site: [`Remyxjs.com`](https://remyxjs.com)

## Packages

| Package                                                        | Version    | Description                                                             |
| -------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------- |
| [`@remyxjs/core`](https://www.npmjs.com/package/@remyxjs/core) | 1.3.0-beta | Framework-agnostic engine, commands, plugins, utilities, and CSS themes |
| [`@remyxjs/react`](./)                                         | 1.3.0-beta | React components, hooks, and TypeScript declarations                    |

Use `@remyxjs/core` directly if building a wrapper for another framework (Vue, Svelte, Angular, vanilla JS).

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Features](#features)
- [Props](#props)
- [Config File](#config-file)
- [Toolbar](#toolbar)
  - [Custom Toolbar](#custom-toolbar-via-props)
  - [Presets](#toolbar-presets)
  - [Helper Functions](#toolbar-helper-functions)
  - [Available Items](#available-toolbar-items)
- [Menu Bar](#menu-bar)
- [Multiple Editors](#multiple-editors)
- [Fonts](#font-configuration)
- [Status Bar](#status-bar)
- [Theming](#theming)
  - [Custom Themes](#custom-themes)
  - [Theme Presets](#theme-presets)
  - [Theme Variables](#available-theme-variables)
  - [Per-Item Toolbar Theming](#per-item-toolbar-theming)
- [Paste Handling](#paste--auto-conversion)
- [File Uploads](#file-uploads)
- [Output Formats](#output-formats)
- [Attaching to Existing Elements](#attaching-to-existing-elements)
- [Plugins](#plugins)
- [Read-Only Mode](#read-only-mode)
- [Error Handling](#error-handling)
- [Engine Access](#engine-access)
- [Import & Export Documents](#import--export-documents)
- [Sanitization](#sanitization)
- [Keyboard Shortcuts](#keyboard-shortcuts)
- [Heading Level Offset](#heading-level-offset)
- [Floating Toolbar & Context Menu](#floating-toolbar--context-menu)
- [Autosave](#autosave)
- [UX/UI Features](#uxui-features)
- [Collaboration](#collaboration)
- [Form Integration](#form-integration)
- [Exports](#exports)
- [TypeScript](#typescript)
- [Using `@remyxjs/core` Directly](#using-remyxcore-directly)

## Installation

```bash
npm install @remyxjs/core @remyxjs/react
```

Then scaffold the `remyxjs/` directory in your project root:

```bash
npx remyxjs init
```

This creates `remyxjs/config/`, `remyxjs/plugins/`, and `remyxjs/themes/` with built-in presets, plugins, and theme CSS files. The editor discovers these at runtime via Vite's `import.meta.glob()`. See the [setup guide](../docs/SETUP.md) for details and CLI options (`--force`, `--no-plugins`, `--no-themes`).

Import both stylesheets in your app entry point:

```js
import '@remyxjs/core/style.css'; // theme variables, light/dark themes
import '@remyxjs/react/style.css'; // component styles (toolbar, modals, etc.)
```

## Quick Start

```jsx
import { useState } from 'react';
import { RemyxEditor } from '@remyxjs/react';
import '@remyxjs/core/style.css';
import '@remyxjs/react/style.css';

function App() {
  const [content, setContent] = useState('');

  return (
    <RemyxEditor
      value={content}
      onChange={setContent}
      placeholder="Start typing..."
      height={400}
    />
  );
}
```

### Uncontrolled Mode

```jsx
<RemyxEditor
  defaultValue="<p>Initial content</p>"
  onChange={(html) => console.log(html)}
/>
```

### Markdown Mode

```jsx
const [markdown, setMarkdown] = useState(
  '# Hello\n\nStart typing...',
);

<RemyxEditor
  value={markdown}
  onChange={setMarkdown}
  outputFormat="markdown"
/>;
```

### With Upload Handler

```jsx
<RemyxEditor
  value={content}
  onChange={setContent}
  uploadHandler={async (file) => {
    const formData = new FormData();
    formData.append('file', file);
    const res = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
    });
    const { url } = await res.json();
    return url;
  }}
/>
```

### Dark Theme with Google Fonts

```jsx
<RemyxEditor
  theme="dark"
  googleFonts={['Inter', 'Fira Code', 'Merriweather']}
  height={500}
/>
```

## Features

- **Rich text editing** — bold, italic, underline, strikethrough, subscript, superscript, headings, lists, blockquotes, code blocks, tables (with sorting, filtering, formulas, cell formatting, resize handles, and clipboard interop), and horizontal rules
- **Toolbar** — fully configurable with presets, helper functions, and per-item theming
- **Menu bar** — application-style menus (File, Edit, View, Insert, Format) with submenus and auto-deduplication
- **Markdown** — bidirectional HTML/Markdown conversion, toggle between modes, and auto-detection on paste
- **Theming** — light/dark mode, 4 built-in presets (Ocean, Forest, Sunset, Rose), full CSS variable customization
- **File uploads** — images and attachments via drag-and-drop, paste, or toolbar with pluggable upload handlers (S3, R2, GCS, custom)
- **Fonts** — custom font lists, Google Fonts auto-loading, and helper functions
- **Code block syntax highlighting** — 11 languages with auto-detection, theme-aware colors, language selector dropdown, line numbers toggle, copy-to-clipboard, inline code highlighting, and extensible language registry
- **Enhanced tables** — sortable columns, multi-column sort, filterable rows, inline formulas, cell formatting, column/row resize, sticky headers, and Excel/Sheets clipboard interop via `TablePlugin`
- **Template system** — `{{merge_tag}}` chips, `{{#if}}`/`{{#each}}` blocks, live preview, 5 pre-built templates, custom template library via `TemplatePlugin`
- **Keyboard-first editing** — Vim (normal/insert/visual), Emacs keybindings, custom key map, multi-cursor (`Cmd+D`), smart auto-pairing, jump-to-heading via `KeyboardPlugin`
- **Drag-and-drop content** — Drop zones, cross-editor drag, file/image/rich-text drops, block reorder with ghost preview via `DragDropPlugin`
- **Advanced link management** — Link previews, broken link detection, auto-linking, click analytics, bookmark anchors via `LinkPlugin`
- **Callouts & alerts** — 7 built-in callout types with custom type registration, collapsible toggle, nested content, and GitHub-flavored alert syntax auto-conversion via `CalloutPlugin`
- **Comments & annotations** — Inline comment threads via `CommentsPlugin`, `CommentsPanel` sidebar with thread cards/replies/actions, `useComments` hook for reactive state, @mention parsing, resolved/unresolved state, comment-only mode, import/export
- **Real-time collaboration** — CRDT-based co-editing with live cursors, presence indicators, offline-first sync, configurable transport (WebSocket, WebRTC, or custom) via `CollaborationPlugin`, `useCollaboration` hook, `CollaborationBar` component
- **Plugins** — `createPlugin()` API with hooks for commands, toolbar items, status bar items, and context menus
- **Config file** — JSON config files in `remyxjs/config/` with 5 built-in presets (`default`, `minimal`, `blog-editor`, `full-toolbar`, `toolbar-and-menu`)
- **Block-based editing** — block-level toolbar with type conversion, drag-to-reorder, collapsible sections, block grouping, `BlockTemplatePlugin` with built-in templates
- **Mobile & touch** — touch floating toolbar, swipe indent/outdent, long-press context menu with haptic feedback, pinch-to-zoom, responsive toolbar overflow, virtual keyboard-aware layout
- **Multi-instance** — unlimited editors per page with full isolation (state, events, DOM, modals), `EditorBus` for inter-editor communication, `SharedResources` for memory-efficient shared schemas and icons
- **Import/Export** — PDF, DOCX, Markdown, CSV, and HTML
- **Paste cleaning** — intelligent cleanup from Word, Google Docs, LibreOffice, Pages, and raw markdown
- **Command palette** — searchable overlay with all editor commands, opened via `Mod+Shift+P` or toolbar button
- **Keyboard shortcuts** — customizable with sensible defaults
- **Accessibility** — semantic HTML, ARIA attributes, keyboard navigation, and focus management

## Props

| Prop                  | Type                                                             | Default       | Description                                                                               |
| --------------------- | ---------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------- |
| `config`              | `string`                                                         | —             | Config file name from `remyxjs/config/` (e.g., `'default'`, `'minimal'`, `'blog-editor'`) |
| `value`               | `string`                                                         | —             | Controlled content (HTML or Markdown)                                                     |
| `defaultValue`        | `string`                                                         | —             | Initial content for uncontrolled mode                                                     |
| `onChange`            | `(content: string) => void`                                      | —             | Called when content changes                                                               |
| `outputFormat`        | `'html' \| 'markdown'`                                           | `'html'`      | Format passed to `onChange`                                                               |
| `toolbar`             | `string[][]`                                                     | Full toolbar  | Custom toolbar layout                                                                     |
| `menuBar`             | `boolean \| MenuBarConfig[]`                                     | —             | Enable menu bar                                                                           |
| `theme`               | `'light' \| 'dark' \| 'ocean' \| 'forest' \| 'sunset' \| 'rose'` | `'light'`     | Editor theme                                                                              |
| `placeholder`         | `string`                                                         | `''`          | Placeholder text                                                                          |
| `height`              | `number`                                                         | `300`         | Editor height in px                                                                       |
| `minHeight`           | `number`                                                         | —             | Minimum height                                                                            |
| `maxHeight`           | `number`                                                         | —             | Maximum height (scrolls)                                                                  |
| `readOnly`            | `boolean`                                                        | `false`       | Disable editing                                                                           |
| `fonts`               | `string[]`                                                       | Built-in list | Custom font families                                                                      |
| `googleFonts`         | `string[]`                                                       | —             | Google Font families to auto-load                                                         |
| `statusBar`           | `'bottom' \| 'top' \| 'popup' \| false`                          | `'bottom'`    | Word/character count position                                                             |
| `customTheme`         | `object`                                                         | —             | CSS variable overrides                                                                    |
| `toolbarItemTheme`    | `object`                                                         | —             | Per-item toolbar styling                                                                  |
| `floatingToolbar`     | `boolean`                                                        | `true`        | Show toolbar on text selection                                                            |
| `contextMenu`         | `boolean`                                                        | `true`        | Show right-click context menu                                                             |
| `commandPalette`      | `boolean`                                                        | `true`        | Enable command palette (Mod+Shift+P or toolbar button)                                    |
| `autosave`            | `boolean \| AutosaveConfig`                                      | `false`       | Enable autosave with optional config (storage provider, interval, key)                    |
| `emptyState`          | `boolean \| ReactNode`                                           | `false`       | Show empty state when editor has no content (true for default, or custom React node)      |
| `breadcrumb`          | `boolean`                                                        | `false`       | Show breadcrumb bar with DOM path to current selection                                    |
| `minimap`             | `boolean`                                                        | `false`       | Show minimap preview on right edge                                                        |
| `splitViewFormat`     | `'html' \| 'markdown'`                                           | `'html'`      | Format for split view preview pane                                                        |
| `customizableToolbar` | `boolean`                                                        | `false`       | Enable drag-and-drop toolbar button rearrangement                                         |
| `onToolbarChange`     | `(order: string[]) => void`                                      | —             | Called when toolbar order changes via drag                                                |
| `plugins`             | `Plugin[]`                                                       | —             | Custom plugins                                                                            |
| `uploadHandler`       | `(file: File) => Promise<string>`                                | —             | File upload handler                                                                       |
| `shortcuts`           | `object`                                                         | —             | Keyboard shortcut overrides                                                               |
| `sanitize`            | `object`                                                         | —             | HTML sanitization options                                                                 |
| `attachTo`            | `React.RefObject`                                                | —             | Attach to an existing element                                                             |
| `onReady`             | `(engine) => void`                                               | —             | Called when editor initializes                                                            |
| `onFocus`             | `() => void`                                                     | —             | Called on focus                                                                           |
| `onBlur`              | `() => void`                                                     | —             | Called on blur                                                                            |
| `className`           | `string`                                                         | `''`          | CSS class for wrapper                                                                     |
| `style`               | `object`                                                         | —             | Inline styles for wrapper                                                                 |

## Config File

Each `<RemyxEditor />` instance is driven by a JSON config file in `remyxjs/config/`. The `config` prop references the file name (without `.json`).

### Built-in Presets

5 config presets ship out of the box:

```jsx
<RemyxEditor config="default" />          {/* Full-featured editor */}
<RemyxEditor config="minimal" />          {/* Stripped-down toolbar */}
<RemyxEditor config="blog-editor" />      {/* Blog-focused layout */}
<RemyxEditor config="full-toolbar" />     {/* All toolbar items */}
<RemyxEditor config="toolbar-and-menu" /> {/* Toolbar + menu bar */}
```

### Custom Config

Create a JSON file in `remyxjs/config/` (e.g., `remyxjs/config/my-editor.json`):

```json
{
  "theme": "dark",
  "placeholder": "Start writing...",
  "height": 400,
  "toolbar": [
    ["bold", "italic", "underline"],
    ["link", "image"]
  ],
  "plugins": {
    "syntax-highlight": true,
    "table": true
  }
}
```

Then use it:

```jsx
<RemyxEditor config="my-editor" />
```

### Config Resolution Priority

Values merge with this priority (highest first):

1. **Component props** — always win (e.g., `<RemyxEditor config="default" theme="dark" />`)
2. **Config file** — values from the JSON file in `remyxjs/config/`
3. **Built-in defaults** — `theme: 'light'`, `height: 300`, etc.

## Toolbar

### Default Toolbar

```js
[
  ['undo', 'redo'],
  ['headings', 'fontFamily', 'fontSize'],
  ['bold', 'italic', 'underline', 'strikethrough'],
  ['foreColor', 'backColor'],
  ['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'],
  ['orderedList', 'unorderedList', 'taskList'],
  ['outdent', 'indent'],
  [
    'link',
    'image',
    'table',
    'embedMedia',
    'blockquote',
    'codeBlock',
    'horizontalRule',
  ],
  ['subscript', 'superscript'],
  [
    'findReplace',
    'toggleMarkdown',
    'sourceMode',
    'export',
    'fullscreen',
  ],
];
```

Each inner array is a visual group separated by a divider.

### Custom Toolbar via Props

```jsx
<RemyxEditor
  toolbar={[
    ['bold', 'italic', 'underline'],
    ['orderedList', 'unorderedList'],
    ['link', 'image'],
  ]}
/>
```

### Toolbar Presets

```jsx
import { TOOLBAR_PRESETS } from '@remyxjs/react';

<RemyxEditor toolbar={TOOLBAR_PRESETS.full} />      // all items including plugin commands (default)
<RemyxEditor toolbar={TOOLBAR_PRESETS.rich} />       // all features with callout, math, toc, bookmark, merge tag
<RemyxEditor toolbar={TOOLBAR_PRESETS.standard} />   // common editing without plugins
<RemyxEditor toolbar={TOOLBAR_PRESETS.minimal} />    // headings, basics, lists, links, images
<RemyxEditor toolbar={TOOLBAR_PRESETS.bare} />       // bold, italic, underline only
```

### Toolbar Helper Functions

#### `removeToolbarItems(config, itemsToRemove)`

```jsx
import { DEFAULT_TOOLBAR, removeToolbarItems } from '@remyxjs/react';

const toolbar = removeToolbarItems(DEFAULT_TOOLBAR, [
  'image',
  'table',
  'embedMedia',
  'export',
]);
```

#### `addToolbarItems(config, items, options?)`

```jsx
import { TOOLBAR_PRESETS, addToolbarItems } from '@remyxjs/react';

addToolbarItems(TOOLBAR_PRESETS.minimal, ['fullscreen']); // append as new group
addToolbarItems(TOOLBAR_PRESETS.minimal, 'fullscreen', { group: -1 }); // add to last group
addToolbarItems(TOOLBAR_PRESETS.minimal, 'taskList', {
  after: 'unorderedList',
});
addToolbarItems(TOOLBAR_PRESETS.minimal, 'strikethrough', {
  before: 'underline',
});
```

#### `createToolbar(items)`

Build a toolbar from a flat list — items are auto-grouped by category:

```jsx
import { createToolbar } from '@remyxjs/react';

const toolbar = createToolbar([
  'bold',
  'italic',
  'underline',
  'headings',
  'link',
  'image',
  'orderedList',
  'unorderedList',
  'fullscreen',
]);
// Groups by category: [['headings'], ['bold', 'italic', 'underline'], ['orderedList', 'unorderedList'], ['link', 'image'], ['fullscreen']]
```

### Available Toolbar Items

| Item                 | Type         | Description                                                          |
| -------------------- | ------------ | -------------------------------------------------------------------- |
| `undo`               | Button       | Undo last action                                                     |
| `redo`               | Button       | Redo last action                                                     |
| `headings`           | Dropdown     | Block type (Normal, H1–H6)                                           |
| `fontFamily`         | Dropdown     | Font family selector                                                 |
| `fontSize`           | Dropdown     | Font size selector                                                   |
| `bold`               | Button       | Bold                                                                 |
| `italic`             | Button       | Italic                                                               |
| `underline`          | Button       | Underline                                                            |
| `strikethrough`      | Button       | Strikethrough                                                        |
| `subscript`          | Button       | Subscript                                                            |
| `superscript`        | Button       | Superscript                                                          |
| `foreColor`          | Color Picker | Text color                                                           |
| `backColor`          | Color Picker | Background color                                                     |
| `alignLeft`          | Button       | Align left                                                           |
| `alignCenter`        | Button       | Align center                                                         |
| `alignRight`         | Button       | Align right                                                          |
| `alignJustify`       | Button       | Justify                                                              |
| `orderedList`        | Button       | Numbered list                                                        |
| `unorderedList`      | Button       | Bulleted list                                                        |
| `taskList`           | Button       | Task/checkbox list                                                   |
| `indent`             | Button       | Increase indent                                                      |
| `outdent`            | Button       | Decrease indent                                                      |
| `link`               | Button       | Insert/edit link                                                     |
| `image`              | Button       | Insert image                                                         |
| `attachment`         | Button       | Attach file                                                          |
| `importDocument`     | Button       | Import (PDF, DOCX, MD, CSV)                                          |
| `table`              | Button       | Insert table                                                         |
| `embedMedia`         | Button       | Embed video/media                                                    |
| `blockquote`         | Button       | Block quote                                                          |
| `codeBlock`          | Button       | Code block                                                           |
| `horizontalRule`     | Button       | Horizontal divider                                                   |
| `findReplace`        | Button       | Find and replace                                                     |
| `toggleMarkdown`     | Button       | Toggle markdown mode                                                 |
| `sourceMode`         | Button       | View/edit HTML source                                                |
| `export`             | Button       | Export (PDF, MD, DOCX)                                               |
| `commandPalette`     | Button       | Open command palette                                                 |
| `fullscreen`         | Button       | Toggle fullscreen                                                    |
| `insertCallout`      | Button       | Insert callout block                                                 |
| `insertMath`         | Button       | Insert math equation                                                 |
| `insertToc`          | Button       | Insert table of contents                                             |
| `insertBookmark`     | Button       | Insert bookmark anchor                                               |
| `insertMergeTag`     | Button       | Insert merge tag                                                     |
| `toggleAnalytics`    | Button       | Toggle analytics panel                                               |
| `toggleSpellcheck`   | Button       | Toggle spellcheck                                                    |
| `checkGrammar`       | Button       | Run grammar check                                                    |
| `addComment`         | Button       | Add inline comment on selection                                      |
| `removeFormat`       | Button       | Remove all inline formatting                                         |
| `distractionFree`    | Button       | Toggle distraction-free mode (Mod+Shift+D)                           |
| `toggleSplitView`    | Button       | Toggle side-by-side preview (Mod+Shift+V)                            |
| `typography`         | Dropdown     | Typography controls (line height, letter spacing, paragraph spacing) |
| `lineHeight`         | Dropdown     | Line height adjustment                                               |
| `letterSpacing`      | Dropdown     | Letter spacing adjustment                                            |
| `paragraphSpacing`   | Dropdown     | Paragraph spacing adjustment                                         |
| `startCollaboration` | Button       | Start real-time collaboration session                                |
| `stopCollaboration`  | Button       | Stop collaboration session                                           |

## Menu Bar

Add an application-style menu bar above the toolbar. Both the toolbar and menu bar display all their items — the menu bar is an additional navigation layer, not a replacement for the toolbar.

### Enable

```jsx
<RemyxEditor menuBar={true} />
```

**Default menus:**

| Menu       | Items                                                                                                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **File**   | Import Document, Export Document                                                                                                                                              |
| **Edit**   | Undo, Redo, Find & Replace                                                                                                                                                    |
| **View**   | Fullscreen, Distraction-Free Mode, Split View, Toggle Markdown, Source Mode, Toggle Analytics                                                                                 |
| **Insert** | Link, Image, Table, Attachment, Embed Media, Blockquote, Code Block, Horizontal Rule, Insert Callout, Insert Math, Insert TOC, Insert Bookmark, Insert Merge Tag, Add Comment |
| **Format** | Bold, Italic, Underline, Strikethrough, Subscript, Superscript, Alignment, Lists, Colors, Typography (Line Height, Letter Spacing, Paragraph Spacing), Remove Formatting      |

### Custom Menu Bar

```jsx
<RemyxEditor
  menuBar={[
    { label: 'File', items: ['importDocument', 'export'] },
    { label: 'Edit', items: ['undo', 'redo', '---', 'findReplace'] },
    {
      label: 'Format',
      items: [
        'bold',
        'italic',
        'underline',
        '---',
        {
          label: 'Alignment',
          items: [
            'alignLeft',
            'alignCenter',
            'alignRight',
            'alignJustify',
          ],
        },
      ],
    },
  ]}
/>
```

Each menu object has a `label` (string) and `items` array of command names, `'---'` separators, or nested `{ label, items }` submenu objects. Any command from the [Available Toolbar Items](#available-toolbar-items) table works as a menu item.

### Menu Bar with Config File

In your config JSON (`remyxjs/config/my-editor.json`):

```json
{
  "menuBar": [
    { "label": "Edit", "items": ["undo", "redo"] },
    { "label": "Format", "items": ["bold", "italic", "underline"] }
  ],
  "toolbar": [["headings", "fontFamily", "fontSize"]]
}
```

### Toolbar and Menu Bar

When both the toolbar and menu bar are enabled, the full toolbar is preserved. The menu bar provides an additional navigation layer — it does not remove items from the toolbar. Both surfaces can be used together to give users maximum flexibility.

### Menu Bar Behavior

- **Click** a trigger to open, **hover** to switch between open menus
- **Escape** or click outside to close
- Submenus open on hover with a chevron
- Toggle commands show active state, modal commands open their dialogs
- Inherits the editor's theme and custom theme variables

## Multiple Editors

Multiple `<RemyxEditor />` instances work on the same page without any additional setup. Each editor is fully isolated.

### Basic Usage

```jsx
<RemyxEditor placeholder="Editor 1..." height={300} />
<RemyxEditor placeholder="Editor 2..." height={300} />
<RemyxEditor placeholder="Editor 3..." height={200} />
```

### What's Isolated Per Instance

| Feature                | Isolation                                                         |
| ---------------------- | ----------------------------------------------------------------- |
| **Content & state**    | Separate undo/redo history, content, and selection                |
| **Toolbar**            | Independent dropdowns, color pickers, and active states           |
| **Menu bar**           | Menus open/close independently per editor                         |
| **Modals**             | Each editor has its own modals (link, image, table, find/replace) |
| **Floating toolbar**   | Appears only for the editor with an active selection              |
| **Context menu**       | Scoped to the clicked editor                                      |
| **Fullscreen**         | Each editor enters/exits fullscreen independently                 |
| **Plugins**            | Per-editor plugin instances with separate state                   |
| **Events**             | Separate `EventBus` per instance                                  |
| **Keyboard shortcuts** | Fire only for the focused editor                                  |

### Mixed Configurations

```jsx
<RemyxEditor config="default" menuBar={true} height={400} />
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
  <RemyxEditor config="minimal" />
  <RemyxEditor config="blog-editor" />
</div>
<RemyxEditor config="default" theme="dark" placeholder="Standalone editor" />
```

No hard limit on editor count. Performance scales linearly. All runtime state is per-instance.

## Font Configuration

### Custom Font List

```jsx
<RemyxEditor
  fonts={['Arial', 'Georgia', 'Courier New', 'My Custom Font']}
/>
```

### Google Fonts

```jsx
<RemyxEditor googleFonts={['Roboto', 'Open Sans', 'Lato', 'Montserrat']} />

// With custom fonts
<RemyxEditor fonts={['Arial', 'Georgia']} googleFonts={['Poppins', 'Inter']} />

// Specific weights
<RemyxEditor googleFonts={['Roboto:wght@400;700']} />
```

Google Fonts are auto-loaded via CDN and merged into the font dropdown.

### Font Helper Functions

```jsx
import {
  DEFAULT_FONTS,
  removeFonts,
  addFonts,
  loadGoogleFonts,
} from '@remyxjs/react';

// Remove fonts
const fonts = removeFonts(DEFAULT_FONTS, ['Comic Sans MS', 'Impact']);

// Add fonts (append by default, or { position: 'start' } to prepend)
const fonts = addFonts(DEFAULT_FONTS, ['My Custom Font']);

// Load Google Fonts programmatically
loadGoogleFonts(['Roboto', 'Lato']);
```

## Status Bar

The status bar displays word and character counts.

```jsx
<RemyxEditor statusBar="bottom" />  {/* below content (default) */}
<RemyxEditor statusBar="top" />     {/* above content */}
<RemyxEditor statusBar="popup" />   {/* toolbar button with popover */}
<RemyxEditor statusBar={false} />   {/* hidden */}
```

## Theming

```jsx
<RemyxEditor theme="light" />  // default
<RemyxEditor theme="dark" />
```

### Custom Themes

Override CSS variables using `createTheme()` or raw variable names. Custom themes layer on top of the base theme — only specify what you want to change.

```jsx
import { createTheme } from '@remyxjs/react';

const brandTheme = createTheme({
  bg: '#1a1a2e',
  text: '#e0e0e0',
  primary: '#e94560',
  primaryHover: '#c81e45',
  toolbarBg: '#16213e',
  toolbarIcon: '#94a3b8',
  toolbarIconActive: '#e94560',
  radius: '12px',
  contentFontSize: '18px',
});

<RemyxEditor theme="dark" customTheme={brandTheme} />;
```

Or pass raw CSS variables directly:

```jsx
<RemyxEditor
  customTheme={{ '--rmx-primary': '#e94560', '--rmx-bg': '#1a1a2e' }}
/>
```

### Theme Presets

All 6 built-in themes are available via the `theme` prop:

```jsx
<RemyxEditor theme="light" />   {/* Default — clean white */}
<RemyxEditor theme="dark" />    {/* Neutral dark */}
<RemyxEditor theme="ocean" />   {/* Deep blue */}
<RemyxEditor theme="forest" />  {/* Green earth-tone */}
<RemyxEditor theme="sunset" />  {/* Warm orange/amber */}
<RemyxEditor theme="rose" />    {/* Soft pink */}
```

Override individual variables on top of any theme with `customTheme`:

```jsx
import { createTheme } from '@remyxjs/react';

<RemyxEditor
  theme="ocean"
  customTheme={createTheme({ primary: '#ff6b6b' })}
/>;
```

### Available Theme Variables

| Key                   | CSS Variable                  | Description                 |
| --------------------- | ----------------------------- | --------------------------- |
| `bg`                  | `--rmx-bg`                    | Editor background           |
| `text`                | `--rmx-text`                  | Primary text color          |
| `textSecondary`       | `--rmx-text-secondary`        | Muted text color            |
| `border`              | `--rmx-border`                | Border color                |
| `borderSubtle`        | `--rmx-border-subtle`         | Subtle border color         |
| `toolbarBg`           | `--rmx-toolbar-bg`            | Toolbar background          |
| `toolbarBorder`       | `--rmx-toolbar-border`        | Toolbar border              |
| `toolbarButtonHover`  | `--rmx-toolbar-button-hover`  | Button hover background     |
| `toolbarButtonActive` | `--rmx-toolbar-button-active` | Button active background    |
| `toolbarIcon`         | `--rmx-toolbar-icon`          | Icon color                  |
| `toolbarIconActive`   | `--rmx-toolbar-icon-active`   | Active icon color           |
| `primary`             | `--rmx-primary`               | Primary accent color        |
| `primaryHover`        | `--rmx-primary-hover`         | Primary hover color         |
| `primaryLight`        | `--rmx-primary-light`         | Light primary (backgrounds) |
| `focusRing`           | `--rmx-focus-ring`            | Focus outline color         |
| `selection`           | `--rmx-selection`             | Text selection color        |
| `danger`              | `--rmx-danger`                | Error/danger color          |
| `dangerLight`         | `--rmx-danger-light`          | Light danger color          |
| `placeholder`         | `--rmx-placeholder`           | Placeholder text color      |
| `modalBg`             | `--rmx-modal-bg`              | Modal background            |
| `modalOverlay`        | `--rmx-modal-overlay`         | Modal overlay               |
| `statusbarBg`         | `--rmx-statusbar-bg`          | Status bar background       |
| `statusbarText`       | `--rmx-statusbar-text`        | Status bar text             |
| `fontFamily`          | `--rmx-font-family`           | UI font stack               |
| `fontSize`            | `--rmx-font-size`             | UI font size                |
| `contentFontSize`     | `--rmx-content-font-size`     | Content font size           |
| `contentLineHeight`   | `--rmx-content-line-height`   | Content line height         |
| `radius`              | `--rmx-radius`                | Border radius               |
| `radiusSm`            | `--rmx-radius-sm`             | Small border radius         |
| `spacingXs`           | `--rmx-spacing-xs`            | Extra small spacing         |
| `spacingSm`           | `--rmx-spacing-sm`            | Small spacing               |
| `spacingMd`           | `--rmx-spacing-md`            | Medium spacing              |

### Per-Item Toolbar Theming

Style individual toolbar buttons independently using `toolbarItemTheme`.

```jsx
import { createToolbarItemTheme } from '@remyxjs/react';

const itemTheme = createToolbarItemTheme({
  bold: {
    color: '#e11d48',
    activeColor: '#be123c',
    activeBackground: '#ffe4e6',
    borderRadius: '50%',
  },
  italic: {
    color: '#7c3aed',
    activeColor: '#6d28d9',
    activeBackground: '#ede9fe',
  },
  underline: {
    color: '#0891b2',
    activeColor: '#0e7490',
    activeBackground: '#cffafe',
  },
  _separator: { color: '#c4b5fd', width: '2px' },
});

<RemyxEditor toolbarItemTheme={itemTheme} />;
```

Both `customTheme` and `toolbarItemTheme` can be used together — `customTheme` sets global styles, `toolbarItemTheme` overrides specific items.

**Per-item style properties:**

| Key                | CSS Variable            | Description            |
| ------------------ | ----------------------- | ---------------------- |
| `color`            | `--rmx-tb-color`        | Icon/text color        |
| `background`       | `--rmx-tb-bg`           | Default background     |
| `hoverColor`       | `--rmx-tb-hover-color`  | Color on hover         |
| `hoverBackground`  | `--rmx-tb-hover-bg`     | Background on hover    |
| `activeColor`      | `--rmx-tb-active-color` | Color when active      |
| `activeBackground` | `--rmx-tb-active-bg`    | Background when active |
| `border`           | `--rmx-tb-border`       | Border shorthand       |
| `borderRadius`     | `--rmx-tb-radius`       | Border radius          |
| `size`             | `--rmx-tb-size`         | Button width & height  |
| `iconSize`         | `--rmx-tb-icon-size`    | Icon size              |
| `padding`          | `--rmx-tb-padding`      | Button padding         |
| `opacity`          | `--rmx-tb-opacity`      | Button opacity         |

**Separator properties** (via `_separator` key):

| Key      | CSS Variable          | Description      |
| -------- | --------------------- | ---------------- |
| `color`  | `--rmx-tb-sep-color`  | Separator color  |
| `width`  | `--rmx-tb-sep-width`  | Separator width  |
| `height` | `--rmx-tb-sep-height` | Separator height |
| `margin` | `--rmx-tb-sep-margin` | Separator margin |

## Paste & Auto-Conversion

The editor cleans and normalizes pasted content automatically.

| Source             | Handling                                                                                                    |
| ------------------ | ----------------------------------------------------------------------------------------------------------- |
| **Microsoft Word** | Strips Office XML, `mso-*` styles, conditional comments. Converts Word-style lists to proper `<ul>`/`<ol>`. |
| **Google Docs**    | Removes internal IDs/classes. Converts styled spans to semantic tags (`<strong>`, `<em>`, `
`).           |
| **LibreOffice**    | Strips namespace tags and auto-generated class names.                                                       |
| **Apple Pages**    | Removes iWork-specific attributes.                                                                          |
| **Markdown**       | Auto-detects and converts to rich HTML (headings, lists, bold, links, code fences, etc.).                   |
| **Plain text**     | Wraps in `<p>` tags with `<br>` for line breaks.                                                            |
| **Images**         | Inserted as base64 data URIs, or uploaded via `uploadHandler` if configured.                                |

All paste paths (keyboard, drag-and-drop, context menu) share the same pipeline: HTML goes through `cleanPastedHTML()` then `Sanitizer.sanitize()`. Plain text is checked by `looksLikeMarkdown()` and converted if detected.

```js
import { cleanPastedHTML, looksLikeMarkdown } from '@remyxjs/react';

const clean = cleanPastedHTML(dirtyHtml);
const isMarkdown = looksLikeMarkdown(text);
```

## File Uploads

The `uploadHandler` prop controls where images and file attachments are stored. It receives a `File` and returns a `Promise<string>` with the file URL.

Without an `uploadHandler`, images are inserted as base64 data URIs and the attachment upload tab is disabled.

### Custom Server

```jsx
<RemyxEditor
  uploadHandler={async (file) => {
    const formData = new FormData();
    formData.append('file', file);
    const res = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
    });
    const { url } = await res.json();
    return url;
  }}
/>
```

### S3 / R2 / GCS (Pre-signed URL)

```jsx
<RemyxEditor
  uploadHandler={async (file) => {
    const res = await fetch('/api/presign', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        filename: file.name,
        contentType: file.type,
      }),
    });
    const { uploadUrl, publicUrl } = await res.json();
    await fetch(uploadUrl, {
      method: 'PUT',
      headers: { 'Content-Type': file.type },
      body: file,
    });
    return publicUrl;
  }}
/>
```

### Upload Paths

The handler is called consistently across all upload surfaces:

| Path                            | Result                                 |
| ------------------------------- | -------------------------------------- |
| Image toolbar (Upload tab)      | Inserted as `<img>`                    |
| Attachment toolbar (Upload tab) | Inserted as attachment chip            |
| Drag & drop image/file          | Image as `<img>`, others as attachment |
| Paste image/file                | Same as drag & drop                    |

## Output Formats

```jsx
<RemyxEditor outputFormat="html" onChange={(html) => console.log(html)} />
<RemyxEditor outputFormat="markdown" onChange={(md) => console.log(md)} />
```

Conversion utilities for standalone use:

```js
import { htmlToMarkdown, markdownToHtml } from '@remyxjs/react';

const md = htmlToMarkdown('<h1>Hello</h1><p>World</p>');
const html = markdownToHtml('# Hello\n\nWorld');
```

## Attaching to Existing Elements

Enhance an existing `<textarea>` or `<div>` with a full WYSIWYG editor.

### Textarea

```jsx
const textareaRef = useRef(null);

<textarea ref={textareaRef} defaultValue="Initial content" />
<RemyxEditor attachTo={textareaRef} outputFormat="markdown" height={400} />
```

The textarea is hidden and its value stays in sync for form submission.

### Div

```jsx
const divRef = useRef(null);

<div ref={divRef}>
  <h2>Existing content</h2>
  <p>This will become editable.</p>
</div>
<RemyxEditor attachTo={divRef} theme="dark" height={400} />
```

### `useRemyxEditor` Hook

For lower-level control:

```jsx
import { useRemyxEditor } from '@remyxjs/react';

function MyEditor() {
  const targetRef = useRef(null);
  const { engine, containerRef, editableRef, ready } = useRemyxEditor(
    targetRef,
    {
      onChange: (content) => console.log(content),
      placeholder: 'Type here...',
      theme: 'light',
      height: 400,
    },
  );

  return <textarea ref={targetRef} />;
}
```

Returns `engine` (EditorEngine), `containerRef`, `editableRef`, and `ready` (boolean).

### `useEditorEngine` Hook

The lowest-level hook — manages just the engine lifecycle:

```jsx
import { useEditorEngine } from '@remyxjs/react';

function CustomEditor() {
  const editorRef = useRef(null);
  const engine = useEditorEngine(editorRef, {
    outputFormat: 'html',
    plugins: [],
  });

  return <div ref={editorRef} contentEditable />;
}
```

## Plugins

### Plugins

14 optional plugins are available as drag-and-drop folders in `remyxjs/plugins/`:

- **WordCountPlugin** — word/character counts in the status bar
- **PlaceholderPlugin** — placeholder text when empty
- **AutolinkPlugin** — auto-converts typed URLs to links
- **SyntaxHighlightPlugin** — automatic syntax highlighting for code blocks with language detection, theme-aware colors, `setCodeLanguage`/`getCodeLanguage`/`toggleLineNumbers` commands, copy-to-clipboard button, inline `<code>` mini-highlighting, and extensible language registry (`registerLanguage`/`unregisterLanguage`). When active, a language selector dropdown appears on focused code blocks.
- **TablePlugin** — enhanced table features including column/row resize handles, click-to-sort on header cells (single + multi-column with Shift), per-column filter dropdowns, inline cell formulas (SUM, AVERAGE, COUNT, MIN, MAX, IF, CONCAT), cell formatting (number, currency, percentage, date), and sticky header rows. Adds 6 new commands: `toggleHeaderRow`, `sortTable`, `filterTable`, `clearTableFilters`, `formatCell`, `evaluateFormulas`. Context menu adds Toggle Header Row, Format Cell options, and Clear Filters when right-clicking in a table.

- **CalloutPlugin** — styled callout blocks with 7 built-in types (info, warning, error, success, tip, note, question), custom type registration, collapsible toggle, nested content, GFM alert syntax auto-conversion. Adds 4 commands: `insertCallout`, `removeCallout`, `changeCalloutType`, `toggleCalloutCollapse`. Context menu adds "Insert Callout" and "Remove Callout".
- **CommentsPlugin** — inline comment threads on selected text, resolved/unresolved state with visual indicators, @mention parsing, reply threads, comment-only mode (read-only editor with annotation support), import/export, DOM sync via MutationObserver. Adds 6 new commands: `addComment`, `deleteComment`, `resolveComment`, `replyToComment`, `editComment`, `navigateToComment`. Context menu adds "Add Comment" when text is selected.
- **LinkPlugin** — link preview tooltips (via `onUnfurl` callback), broken link detection with periodic scanning and `rmx-link-broken` indicators, auto-linking of typed URLs/emails/phone numbers on Space/Enter, link click analytics (`onLinkClick`), bookmark anchors (`insertBookmark`, `linkToBookmark`, `getBookmarks`, `removeBookmark`), `scanBrokenLinks`/`getBrokenLinks` commands.
- **TemplatePlugin** — `{{merge_tag}}` syntax with visual chips, `{{#if condition}}...{{/if}}` conditional blocks, `{{#each items}}...{{/each}}` repeatable sections, live preview with sample data, 5 pre-built templates (email, invoice, letter, report, newsletter), `registerTemplate`/`unregisterTemplate`/`getTemplateLibrary`/`getTemplate` APIs, `exportTemplate` JSON export. Adds 6 commands: `insertMergeTag`, `loadTemplate`, `previewTemplate`, `exitPreview`, `exportTemplate`, `getTemplateTags`.
- **KeyboardPlugin** — Vim mode (normal/insert/visual with h/j/k/l/w/b motions), Emacs keybindings (Ctrl+A/E/F/B/K/Y), custom keybinding map, smart bracket/quote auto-pairing with wrap-selection, multi-cursor (`Cmd+D`), jump-to-heading (`Cmd+Shift+G`). Adds 5 commands: `setKeyboardMode`, `getVimMode`, `jumpToHeading`, `getHeadings`, `selectNextOccurrence`.
- **DragDropPlugin** — drop zone overlays with visual guides, cross-editor drag via `text/x-remyx-block` transfer, external file/image/rich-text drops, block reorder with ghost preview and drop indicator, `onDrop`/`onFileDrop` callbacks. Adds 2 commands: `moveBlockUp`, `moveBlockDown` (`Cmd+Shift+Arrow`).
- **MathPlugin** — LaTeX/KaTeX math rendering with `$...$` inline and `$$...$$` block syntax, pluggable `renderMath(latex, displayMode)` callback, 40+ symbol palette across 4 categories (Greek, Operators, Arrows, Common), auto equation numbering, `latexToMathML()` conversion, `parseMathExpressions()` detection. Adds 6 commands: `insertMath`, `editMath`, `insertSymbol`, `getSymbolPalette`, `getMathElements`, `copyMathAs`.
- **TocPlugin** — auto-generated table of contents from H1-H6 heading hierarchy, section numbering (1, 1.1, 1.2), `insertToc` renders linked `<nav>` into document, `scrollToHeading` with smooth scroll, `validateHeadings` detects skipped levels, `onOutlineChange` callback + `toc:change` event. Adds 4 commands: `getOutline`, `insertToc`, `scrollToHeading`, `validateHeadings`.
- **AnalyticsPlugin** — real-time readability scores (Flesch-Kincaid, Gunning Fog, Coleman-Liau, Flesch Reading Ease), reading time estimate, vocabulary level (basic/intermediate/advanced), sentence/paragraph length warnings, goal-based word count with progress tracking, keyword density, SEO hints. `onAnalytics` callback + `analytics:update` event. Adds 3 commands: `getAnalytics`, `getSeoAnalysis`, `getKeywordDensity`.
- **SpellcheckPlugin** — built-in grammar engine with passive voice detection, wordiness patterns, cliche detection, and punctuation checks. Inline red wavy (spelling), blue wavy (grammar), and green dotted (style) underlines. Right-click context menu with correction suggestions, "Ignore", and "Add to Dictionary". Writing-style presets (formal, casual, technical, academic). BCP 47 multi-language support. Optional `customService` interface for LanguageTool/Grammarly. Persistent dictionary via localStorage. `onError`/`onCorrection` callbacks + `spellcheck:update`/`grammar:check` events. Adds 6 commands: `toggleSpellcheck`, `checkGrammar`, `addToDictionary`, `ignoreWord`, `setWritingStyle`, `getSpellcheckStats`.

```jsx
import {
  SyntaxHighlightPlugin,
  TablePlugin,
  CommentsPlugin,
  CalloutPlugin,
  LinkPlugin,
  TemplatePlugin,
  KeyboardPlugin,
  DragDropPlugin,
  MathPlugin,
  TocPlugin,
  AnalyticsPlugin,
  SpellcheckPlugin,
} from '@remyxjs/react';

<RemyxEditor
  plugins={[
    SyntaxHighlightPlugin(),
    TablePlugin(),
    CommentsPlugin(),
    CalloutPlugin(),
    LinkPlugin(),
    TemplatePlugin(),
    KeyboardPlugin(),
    DragDropPlugin(),
    MathPlugin(),
    TocPlugin(),
    AnalyticsPlugin(),
    SpellcheckPlugin(),
  ]}
/>;
```

#### TablePlugin in depth

When `TablePlugin()` is active, every `<table class="rmx-table">` in the editor automatically gains:

| Feature               | How it works                                                                                                                                                                                                                                                                                                                                                                |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Sortable columns**  | Click any `<th>` to cycle through ascending → descending → unsorted. Rows are physically reordered in the DOM so the sort persists in HTML output.                                                                                                                                                                                                                          |
| **Multi-column sort** | Hold **Shift** and click additional headers. A small priority number appears on each sorted header.                                                                                                                                                                                                                                                                         |
| **Sort data types**   | The sort auto-detects numeric, date, or alphabetical data. Pass `dataType` explicitly or provide a custom comparator via the `tableSortComparator` engine option.                                                                                                                                                                                                           |
| **Sort indicators**   | ▲/▼ triangles rendered via CSS `::after` on `<th>` elements with `data-sort-dir` attributes.                                                                                                                                                                                                                                                                                |
| **Filterable rows**   | A small ▽ icon appears in each header cell. Click it to open a filter dropdown with a text input. Rows not matching the filter are hidden (non-destructive — they reappear when the filter is cleared). Multiple columns can be filtered simultaneously (AND logic).                                                                                                        |
| **Column/row resize** | Invisible 6px drag handles appear at column and row borders. Drag to resize. The resize is smooth (rAF-driven) and creates an undo snapshot on mouseup.                                                                                                                                                                                                                     |
| **Inline formulas**   | Type `=SUM(A1:A5)` in any cell. On blur, the formula is stored in a `data-formula` attribute and the computed result is displayed. On focus, the formula text is shown for editing. Supports SUM, AVERAGE, COUNT, MIN, MAX, IF, CONCAT, cell references (A1 notation), ranges, arithmetic, and comparison operators. Circular references are detected and display `#CIRC!`. |
| **Cell formatting**   | Right-click a cell to format as Number, Currency, Percentage, or Date. The raw value is preserved in `data-raw-value`; the display uses `Intl.NumberFormat` / `Intl.DateTimeFormat`.                                                                                                                                                                                        |
| **Sticky header**     | `<thead><th>` cells use `position: sticky` so the header row stays visible when scrolling tall tables.                                                                                                                                                                                                                                                                      |
| **Clipboard interop** | Copying table content produces both HTML and TSV (tab-separated values). Pasting TSV or HTML tables from Excel / Google Sheets inserts data into the grid starting at the caret cell, auto-expanding rows and columns as needed.                                                                                                                                            |

**Custom sort comparator:**

```jsx
<RemyxEditor
  plugins={[TablePlugin()]}
  engineOptions={{
    tableSortComparator: (a, b, dataType, columnIndex) => {
      // Custom comparison logic — return negative, zero, or positive
      return a.localeCompare(b);
    },
  }}
/>
```

**Programmatic commands (via engine ref):**

```jsx
import { useRef } from 'react';

function MyEditor() {
  const engineRef = useRef(null);

  const handleSort = () => {
    engineRef.current?.executeCommand('sortTable', {
      keys: [
        { columnIndex: 0, direction: 'asc' },
        { columnIndex: 2, direction: 'desc', dataType: 'numeric' },
      ],
    });
  };

  const handleFilter = () => {
    engineRef.current?.executeCommand('filterTable', {
      columnIndex: 1,
      filterValue: 'active',
    });
  };

  return (
    <>
      <button onClick={handleSort}>Sort by Name then Score</button>
      <button onClick={handleFilter}>Show active only</button>
      <RemyxEditor
        plugins={[TablePlugin()]}
        onReady={(engine) => {
          engineRef.current = engine;
        }}
      />
    </>
  );
}
```

**Context menu additions** (appear when right-clicking inside a table):

- Toggle Header Row
- Format as Number / Currency / Percentage / Date
- Clear Filters

### CalloutPlugin

The `CalloutPlugin` adds styled callout/alert/admonition blocks with collapsible toggle and GFM alert auto-conversion:

```jsx
import { CalloutPlugin } from '@remyxjs/react';

<RemyxEditor plugins={[CalloutPlugin()]} />;
```

**Insert callouts programmatically:**

```jsx
// Basic callout
engine.executeCommand('insertCallout', { type: 'warning' });

// Collapsible with custom title
engine.executeCommand('insertCallout', {
  type: 'tip',
  collapsible: true,
  title: 'Pro tip',
});

// With pre-populated content
engine.executeCommand('insertCallout', {
  type: 'info',
  content: '<ul><li>Step 1</li><li>Step 2</li></ul>',
});

// Change type of the focused callout
engine.executeCommand('changeCalloutType', 'error');

// Register a custom callout type
import { registerCalloutType } from '@remyxjs/react';
registerCalloutType({
  type: 'security',
  label: 'Security',
  icon: '🔒',
  color: '#dc2626',
});
```

**GitHub-flavored alerts** — blockquotes with `> [!NOTE]`, `> [!WARNING]`, etc. are automatically converted to callout blocks on paste or typing.

**Available types:** `info`, `warning`, `error`, `success`, `tip`, `note`, `question` (plus any custom types).

### CommentsPlugin

The `CommentsPlugin` adds inline comment threads — highlight text, attach discussion threads, resolve/reopen, reply, and @mention other users.

```jsx
import { CommentsPlugin } from '@remyxjs/react';

<RemyxEditor
  plugins={[
    CommentsPlugin({
      onComment: (thread) => saveToServer(thread),
      onResolve: ({ thread, resolved }) => updateServer(thread),
      onDelete: (thread) => deleteFromServer(thread),
      onReply: ({ thread, reply }) => notifyUser(reply),
      mentionUsers: ['alice', 'bob', 'charlie'],
    }),
  ]}
/>;
```

**Comment-only mode** — make the editor read-only but still allow annotations:

```jsx
<RemyxEditor plugins={[CommentsPlugin({ commentOnly: true })]} />
```

#### useComments hook

The `useComments` hook provides reactive state and convenience methods:

```jsx
import { useComments, CommentsPanel } from '@remyxjs/react';

function AnnotatedEditor() {
  const engineRef = useRef(null);
  const {
    threads, activeThread, addComment, deleteComment,
    resolveComment, replyToComment, navigateToComment,
    exportThreads, importThreads,
  } = useComments(engineRef.current);

  return (
    <div style={{ display: 'flex' }}>
      <RemyxEditor
        plugins={[CommentsPlugin({ mentionUsers: ['alice', 'bob'] })]}
        onReady={(engine) => { engineRef.current = engine; }}
        style={{ flex: 1 }}
      />
      <CommentsPanel
        threads={threads}
        activeThread={activeThread}
        onNavigate={navigateToComment}
        onResolve={(id, resolved) => resolveComment(id, resolved)}
        onDelete={deleteComment}
        onReply={(id, params) => replyToComment(id, params)}
        filter="all"  {/* 'all' | 'open' | 'resolved' */}
      />
    </div>
  );
}
```

#### CommentsPanel props

| Prop           | Type                                   | Description                           |
| -------------- | -------------------------------------- | ------------------------------------- |
| `threads`      | `CommentThread[]`                      | All threads (from `useComments`)      |
| `activeThread` | `CommentThread \| null`                | Currently focused thread              |
| `onNavigate`   | `(threadId) => void`                   | Called when a thread card is clicked  |
| `onResolve`    | `(threadId, resolved) => void`         | Called when Resolve/Reopen is clicked |
| `onDelete`     | `(threadId) => void`                   | Called when Delete is clicked         |
| `onReply`      | `(threadId, { author, body }) => void` | Called when a reply is submitted      |
| `filter`       | `'all' \| 'open' \| 'resolved'`        | Which threads to display              |
| `className`    | `string`                               | Additional CSS class                  |

#### Engine events

| Event               | Payload                | When                        |
| ------------------- | ---------------------- | --------------------------- |
| `comment:created`   | `{ thread }`           | New comment added           |
| `comment:resolved`  | `{ thread, resolved }` | Thread resolved or reopened |
| `comment:deleted`   | `{ thread }`           | Thread deleted              |
| `comment:replied`   | `{ thread, reply }`    | Reply added to thread       |
| `comment:updated`   | `{ thread }`           | Thread body edited          |
| `comment:clicked`   | `{ thread, element }`  | Comment highlight clicked   |
| `comment:navigated` | `{ threadId }`         | Scrolled to comment         |
| `comment:imported`  | `{ count }`            | Threads imported            |

### Custom Plugins

```js
import { createPlugin } from '@remyxjs/react';

const MyPlugin = createPlugin({
  name: 'my-plugin',
  version: '1.0.0',
  description: 'Example plugin with lifecycle hooks',
  author: 'You',

  // Lifecycle hooks (auto-wired, sandboxed)
  onContentChange(api) {
    console.log('Content changed:', api.getText().length, 'chars');
  },
  onSelectionChange(api) {
    console.log('Selection:', api.getActiveFormats());
  },

  // Traditional init/destroy
  init(api) {
    /* called once on mount */
  },
  destroy(api) {
    /* called on unmount */
  },

  // Dependencies — ensures 'other-plugin' is initialized first
  dependencies: ['other-plugin'],

  // Scoped settings with validation
  settingsSchema: [
    {
      key: 'maxLength',
      type: 'number',
      label: 'Max Length',
      defaultValue: 5000,
      validate: (v) => v > 0,
    },
    {
      key: 'mode',
      type: 'select',
      label: 'Mode',
      defaultValue: 'auto',
      options: [
        { label: 'Auto', value: 'auto' },
        { label: 'Manual', value: 'manual' },
      ],
    },
  ],
  defaultSettings: { maxLength: 5000, mode: 'auto' },
});
```

```jsx
<RemyxEditor plugins={[MyPlugin]} />
```

Access plugin settings from outside:

```jsx
<RemyxEditor
  plugins={[MyPlugin]}
  onReady={(engine) => {
    engine.plugins.getPluginSetting('my-plugin', 'maxLength'); // 5000
    engine.plugins.setPluginSetting('my-plugin', 'maxLength', 3000); // validates + updates
  }}
/>
```

### Plugin with Custom Commands

```js
const HighlightPlugin = createPlugin({
  name: 'highlight',
  init(engine) {
    engine.executeCommand('highlight');
  },
  commands: [
    {
      name: 'highlight',
      execute(eng) {
        document.execCommand('backColor', false, '#ffeb3b');
      },
    },
  ],
});
```

## Read-Only Mode

Disable editing while keeping the full rendered output visible.

```jsx
<RemyxEditor value={content} readOnly={true} />
```

Combine with a toggle for preview mode:

```jsx
const [editing, setEditing] = useState(true);

<button onClick={() => setEditing(!editing)}>
  {editing ? 'Preview' : 'Edit'}
</button>
<RemyxEditor value={content} onChange={setContent} readOnly={!editing} />
```

## Error Handling

### onError Callback

Catch errors from plugins, the engine, and file uploads without crashing the app:

```jsx
<RemyxEditor
  onError={(error, info) => {
    if (info.source === 'plugin') {
      console.warn(`Plugin "${info.pluginName}" failed:`, error);
    } else if (info.source === 'upload') {
      alert(`Upload failed: ${error.message}`);
    } else if (info.source === 'engine') {
      console.error(`Engine error in ${info.phase}:`, error);
    }
  }}
/>
```

Error sources:

| Source     | Info Fields  | Description                 |
| ---------- | ------------ | --------------------------- |
| `'plugin'` | `pluginName` | Plugin init/destroy failure |
| `'engine'` | `phase`      | Engine initialization error |
| `'upload'` | `file`       | Upload handler rejection    |

### Custom Error Fallback

Replace the default error UI with your own component:

```jsx
<RemyxEditor
  errorFallback={
    <div className="editor-error">
      <p>Something went wrong. Please refresh.</p>
    </div>
  }
/>
```

## Engine Access

Use the `onReady` callback to access the `EditorEngine` instance for programmatic control.

### Execute Commands

```jsx
functi