@glyx-dev/rich-text Stable
A fully-featured rich text editor with bold, italic, underline, cursor, selection, and a formatting toolbar.
npm install @glyx-dev/rich-textBasic usage
import { RichTextEditor, RichTextToolbar } from '@glyx-dev/rich-text'
export default function App() {
return (
<RichTextEditor width={700} height={400} placeholder="Start writing…">
<RichTextToolbar />
</RichTextEditor>
)
}The toolbar must be rendered inside RichTextEditor — it reads editor state from context.
Controlled value
import { useState } from 'react'
import { RichTextEditor, RichTextToolbar, emptyDoc, docToPlainText } from '@glyx-dev/rich-text'
function NoteEditor() {
const [doc, setDoc] = useState(emptyDoc())
return (
<>
<RichTextEditor value={doc} onChange={setDoc} width={700} height={400}>
<RichTextToolbar />
</RichTextEditor>
<Text>{docToPlainText(doc)}</Text>
</>
)
}Props
RichTextEditor
| Prop | Type | Default | Description |
|---|---|---|---|
value | Doc | — | Controlled document value |
onChange | (doc: Doc) => void | — | Called on every edit |
width | number | 600 | Editor width in px |
height | number | 400 | Editor height in px |
fontSize | number | 14 | Default font size for new text |
color | string | '#e8e8f0' | Default text color |
placeholder | string | '' | Shown when document is empty |
style | ViewStyle | — | Additional styles on the outer container |
RichTextToolbar
| Prop | Type | Default | Description |
|---|---|---|---|
style | ViewStyle | — | Additional styles on the toolbar row |
Renders B / I / U buttons. Buttons are active (highlighted) when the current selection already has that format. Clicking toggles the format on the selection.
Keyboard shortcuts
| Key | Action |
|---|---|
| Type | Insert at cursor |
Enter | New paragraph |
Backspace / Delete | Delete character or selection |
← → | Move cursor |
↑ ↓ | Move cursor to previous/next line |
Home / End | Start / end of paragraph |
Shift + any move key | Extend selection |
Ctrl+A | Select all |
Ctrl+C / X / V | Copy / cut / paste (plain text) |
Ctrl+B | Toggle bold on selection |
Ctrl+I | Toggle italic on selection |
Ctrl+U | Toggle underline on selection |
Document model
A document is a plain JSON object — serialize and store it however you like:
type Span = {
text: string
bold?: boolean
italic?: boolean
underline?: boolean
color?: string // CSS color
fontSize?: number // overrides editor default
}
type Paragraph = { spans: Span[] }
type Doc = { paragraphs: Paragraph[] }Utilities
import { emptyDoc, docToPlainText, docFromPlainText } from '@glyx-dev/rich-text'
emptyDoc() // → empty Doc
docToPlainText(doc) // → plain string (paragraphs joined by \n)
docFromPlainText('Hello\nWorld') // → Doc with two paragraphsReading editor state from a sibling component
Use useEditorContext() inside any component rendered as a child of RichTextEditor:
import { useEditorContext } from '@glyx-dev/rich-text'
function WordCount() {
const { doc } = useEditorContext()
const words = docToPlainText(doc).trim().split(/\s+/).filter(Boolean).length
return <Text>{words} words</Text>
}
function App() {
return (
<RichTextEditor width={700} height={400}>
<RichTextToolbar />
<WordCount />
</RichTextEditor>
)
}Persisting documents
import { db } from '@glyx-dev/react'
import { emptyDoc } from '@glyx-dev/rich-text'
// Save
await db.run('UPDATE notes SET body = ? WHERE id = ?', [JSON.stringify(doc), id])
// Load
const [row] = await db.query('SELECT body FROM notes WHERE id = ?', [id])
const doc = row ? JSON.parse(row.body) : emptyDoc()