@sanuabeysekara/richtext-editor
v2.0.5
Published
A fully-featured rich text editor with LaTeX equations and image management for React
Maintainers
Readme
@sanuabeysekara/richtext-editor
A fully-featured React rich text editor with LaTeX equations and image management. Built from scratch without using any editor libraries.
Features
✨ Text Formatting
- Bold, Italic, Underline, ~~Strikethrough~~
- Headings (H1, H2, H3)
- Bullet and numbered lists
- Text alignment (left, center, right)
- Hyperlinks
- Undo/Redo
📐 LaTeX Equations
- Insert inline mathematical equations
- Live preview while typing
- Powered by KaTeX
- Delete with hover button
🖼️ Image Management
- Upload images to your backend
- Inline or block alignment options
- Drag corner handles to resize
- Delete with hover button
- Automatic backend cleanup
- Max 1MB per image
👁️ Viewer Component
- Read-only display component
- Matches editor styling
- Responsive design
- No editing capabilities
🛠️ Developer Features
- HTML code preview
- Copy to clipboard
- Ref methods for programmatic control
- TypeScript ready (types coming soon)
Installation
npm install @sanuabeysekara/richtext-editorNote: KaTeX CSS is automatically bundled - no extra imports needed!
Quick Start
import React, { useRef } from 'react'
import { RichTextEditor } from '@sanuabeysekara/richtext-editor'
import '@sanuabeysekara/richtext-editor/dist/style.css'
function App() {
const editorRef = useRef(null)
const saveContent = () => {
const html = editorRef.current.getCleanedContent()
console.log('Content:', html)
}
return (
<>
<RichTextEditor
ref={editorRef}
apiUrl="http://localhost:8000"
/>
<button onClick={saveContent}>Save</button>
</>
)
}Components
RichTextEditor
The main editing component.
import { RichTextEditor } from '@sanuabeysekara/richtext-editor'
<RichTextEditor
ref={editorRef}
apiUrl="http://localhost:8000"
uploadEndpoint="/images/upload"
deleteEndpoint="/images"
initialContent="<p>Start here...</p>"
onChange={(html) => console.log(html)}
onImageUpload={(data) => console.log(data)}
onImageDelete={(id) => console.log(id)}
/>RichTextViewer
Read-only display component.
import { RichTextViewer } from '@sanuabeysekara/richtext-editor'
<RichTextViewer content={cleanedHtml} />Props
RichTextEditor Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| apiUrl | string | 'http://localhost:8000' | Backend base URL |
| uploadEndpoint | string | '/images/upload' | Image upload endpoint |
| deleteEndpoint | string | '/images' | Image delete endpoint |
| initialContent | string | '<p><br></p>' | Initial HTML content |
| onChange | function | null | Content change callback |
| onImageUpload | function | null | Image upload callback |
| onImageDelete | function | null | Image delete callback |
RichTextViewer Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| content | string | '' | HTML content to display |
| className | string | '' | Additional CSS class |
| style | object | {} | Inline styles |
Ref Methods
const editorRef = useRef(null)
// Get raw HTML (with editor controls)
const raw = editorRef.current.getContent()
// Get cleaned HTML (production-ready)
const clean = editorRef.current.getCleanedContent()
// Set content
editorRef.current.setContent('<p>New content</p>')
// Clear editor
editorRef.current.clear()
// Get plain text
const text = editorRef.current.getText()
// Focus editor
editorRef.current.focus()Backend Requirements
The editor requires a backend API with these endpoints:
Upload Image
POST /images/upload
Content-Type: multipart/form-data
Response:
{
"success": true,
"image_id": "uuid",
"filename": "uuid.jpg",
"url": "/uploads/uuid.jpg",
"size": 123456
}Delete Image
DELETE /images/{image_id}
Response:
{
"success": true,
"message": "Image deleted"
}Backend Example: A Python FastAPI example is included in the repository.
Complete Example
import React, { useRef, useState } from 'react'
import { RichTextEditor, RichTextViewer } from '@sanuabeysekara/richtext-editor'
import '@sanuabeysekara/richtext-editor/dist/style.css'
function MyEditor() {
const editorRef = useRef(null)
const [savedContent, setSavedContent] = useState('')
const [showPreview, setShowPreview] = useState(false)
const handleSave = () => {
const cleaned = editorRef.current.getCleanedContent()
setSavedContent(cleaned)
setShowPreview(true)
// Save to your backend
fetch('/api/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: cleaned })
})
}
return (
<div>
<h1>My Editor</h1>
<RichTextEditor
ref={editorRef}
apiUrl="https://api.mysite.com"
onChange={(html) => console.log('Changed:', html)}
/>
<button onClick={handleSave}>Save</button>
{showPreview && (
<div>
<h2>Preview</h2>
<RichTextViewer content={savedContent} />
</div>
)}
</div>
)
}Styling
Both components come with complete CSS included. No additional styling needed.
To customize, wrap in your own container or use the className and style props.
Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
License
MIT
Contributing
Issues and pull requests are welcome!
Support
For issues, questions, or feature requests, please open an issue on GitHub.
