@trafica/editor
v1.0.84
Published
Production-grade rich text editor for React
Maintainers
Readme
@trafica/editor
Production-grade rich text editor for React. No external editor framework — built from scratch with a custom engine.
Install
npm install @trafica/editorReact 18+ required as a peer dependency.
Quick start
import { Editor } from '@trafica/editor';
import '@trafica/editor/styles';
function App() {
return (
<Editor
placeholder="Start writing…"
onChange={(html) => console.log(html)}
/>
);
}Controlled mode
const [value, setValue] = useState('');
<Editor value={value} onChange={setValue} />Imperative API
import type { EditorAPI } from '@trafica/editor';
const ref = useRef<EditorAPI>(null);
<Editor ref={ref} />
// Methods
ref.current?.getHTML() // → string
ref.current?.setHTML('<p>…</p>')
ref.current?.getMarkdown() // → string
ref.current?.getJSON() // → Document model
ref.current?.focus()
ref.current?.blur()
ref.current?.clear()
ref.current?.undo()
ref.current?.redo()
ref.current?.execCommand('bold')
ref.current?.execCommand('heading', 2)
ref.current?.execCommand('set-font-size', '18px')
ref.current?.execCommand('insert-link', 'https://example.com', 'Link text')
ref.current?.execCommand('insert-table', 3, 4)
ref.current?.registerCommand('my-cmd', (engine) => { /* … */; return true })Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | string | — | Controlled HTML value |
| defaultValue | string | — | Uncontrolled initial HTML |
| onChange | (html: string) => void | — | Called on every content change |
| toolbar | EditorToolbarConfig \| false | DEFAULT_TOOLBAR | Toolbar items or false to hide |
| theme | 'light' \| 'dark' \| ThemeConfig | 'light' | Visual theme |
| placeholder | string | 'Start writing…' | Placeholder text |
| readOnly | boolean | false | Disable editing |
| minHeight | string \| number | — | Min editor height |
| maxHeight | string \| number | — | Max editor height (scrolls) |
| plugins | EditorPlugin[] | — | Custom plugins |
| onFocus | () => void | — | Focus callback |
| onBlur | () => void | — | Blur callback |
| onReady | (api: EditorAPI) => void | — | Fires once after mount |
| onUploadImage | (file: File) => Promise<string> | — | Custom image upload handler |
| fonts | (string \| FontOption)[] | built-in defaults | Font Family dropdown options |
| mergeFonts | boolean | false | Append fonts to defaults instead of replacing |
| className | string | — | Root element class |
| style | CSSProperties | — | Root element style |
Toolbar configuration
import { MINIMAL_TOOLBAR, BASIC_TOOLBAR, DEFAULT_TOOLBAR } from '@trafica/editor';
// Preset configs
<Editor toolbar={MINIMAL_TOOLBAR} />
<Editor toolbar={BASIC_TOOLBAR} />
<Editor toolbar={DEFAULT_TOOLBAR} /> // default
// Custom flat array
<Editor toolbar={['bold', 'italic', 'underline', '|', 'bullet-list', 'ordered-list', '|', 'undo', 'redo']} />
// Hide toolbar
<Editor toolbar={false} />Available toolbar items
bold italic underline strikethrough code link highlight text-color background-color font-family font-size align heading blockquote bullet-list ordered-list check-list code-block horizontal-rule image table undo redo find-replace source special-chars | (separator)
Font family
CKEditor-style Font Family dropdown with live preview and active-font detection. The font list is configurable via the fonts prop.
import type { FontOption } from '@trafica/editor';
interface FontOption {
label: string; // shown in the dropdown
value: string; // CSS font-family applied to the selected text
}// Default list (no prop):
// Arial · Times New Roman · Georgia · Verdana · Tahoma · Trebuchet MS · Courier New
<Editor />
// Replace defaults — object form or plain strings (mixable)
<Editor fonts={[{ label: 'Inter', value: 'Inter' }, 'Poppins', 'Roboto']} />
// Merge with defaults (duplicates dropped by family name)
<Editor mergeFonts fonts={['Inter']} />Selecting Inter applies font-family: Inter and serializes to <span style="font-family:Inter">…</span>; the cursor landing inside that span shows Inter in the toolbar.
The editor does not load font faces — it only applies the
valueas a CSSfont-family. Make the fonts available yourself (Google Fonts,next/font,@font-face, CDN, …).
import { DEFAULT_FONTS, resolveFonts } from '@trafica/editor';
// resolveFonts(fonts?, mergeFonts?) → FontOption[] (compute the list yourself)Theming
// Built-in themes
<Editor theme="light" />
<Editor theme="dark" />
// Custom tokens
<Editor
theme={{
mode: 'light',
tokens: {
bg: '#fdf6e3',
fg: '#657b83',
toolbarBg: '#eee8d5',
border: '#93a1a1',
primary: '#268bd2',
},
}}
/>Import individual theme CSS files to apply globally:
import '@trafica/editor/themes/light';
import '@trafica/editor/themes/dark';Plugin system
import type { EditorPlugin } from '@trafica/editor';
const myPlugin: EditorPlugin = {
keyBindings: {
'Ctrl+Shift+X': (engine) => {
// custom key handler
return true; // handled
},
},
onTransaction: (tr, state) => {
// react to every transaction
},
};
<Editor plugins={[myPlugin]} />Serializers
import { htmlSerializer, jsonSerializer, markdownSerializer } from '@trafica/editor';
htmlSerializer.serialize(doc) // Document → HTML string
htmlSerializer.deserialize(html) // HTML string → Document
jsonSerializer.serialize(doc) // Document → JSON string
markdownSerializer.serialize(doc) // Document → Markdown stringexecCommand reference
| Command | Args | Description |
|---------|------|-------------|
| bold | — | Toggle bold |
| italic | — | Toggle italic |
| underline | — | Toggle underline |
| strikethrough | — | Toggle strikethrough |
| code | — | Toggle inline code |
| blockquote | — | Toggle blockquote |
| code-block | — | Toggle code block |
| paragraph | — | Set paragraph |
| heading | level: 1–6 | Set heading level |
| bullet-list | — | Toggle bullet list |
| ordered-list | — | Toggle ordered list |
| check-list | — | Toggle checklist |
| undo | — | Undo |
| redo | — | Redo |
| clear | — | Clear document |
| set-text-color | color: string \| null | Set text color |
| set-highlight | color: string \| null | Set highlight color |
| set-font-family | family: string \| null | Set font family |
| set-font-size | size: string \| null | Set font size (e.g. '16px') |
| set-alignment | align: 'left' \| 'center' \| 'right' \| 'justify' | Set alignment |
| insert-text | text: string | Insert text at cursor |
| insert-link | href: string, text?: string | Insert link |
| insert-image | src: string, alt?: string | Insert image |
| insert-table | rows: number, cols: number | Insert table |
| insert-horizontal-rule | — | Insert horizontal rule |
License
MIT
