rich-text-lite
v4.0.0
Published
A lightweight, customizable rich text editor React component
Maintainers
Readme
rich-text-lite
A lightweight, zero-dependency (besides React) rich text editor React component with a fully customizable toolbar.
Table of Contents
- Installation
- Quick Start
- Props
- Features
- Toolbar Configuration
- Toolbar Buttons Reference
- CSP / Nonce Support
- Styling & CSS Customization
- Output Format
- Development
Installation
npm install rich-text-liteQuick Start
import { useState } from "react";
import { RichTextEditor } from "rich-text-lite";
import "rich-text-lite/dist/style.css";
function App() {
const [html, setHtml] = useState("");
return (
<RichTextEditor
value={html}
onChange={setHtml}
/>
);
}Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | string | "" | HTML content for the editor |
| onChange | (html: string) => void | — | Called with the updated HTML string on each edit |
| isNonceEnabled | boolean | false | Whether to add a nonce attribute to injected <style> tags (for CSP) |
| nonceValue | string | "" | The nonce value to use when isNonceEnabled is true |
| placeholder | string | "Start typing..." | Placeholder text shown when editor is empty |
| cleanPaste | boolean | true | Sanitizes pasted HTML while keeping common rich text tags |
| toolbarConfig.editor.placeholder | string | "Start typing..." | Editor placeholder from config JSON (overrides default placeholder) |
| toolbarConfig.selectComponent | React.ComponentType | built-in button dropdown | Custom UI component for toolbar dropdowns (heading, font, size, line-height) |
| toolbarConfig.selectOptionComponent | React.ComponentType | built-in <option> | Option item component for selectComponent (e.g. MUI MenuItem) |
| toolbarConfig.dialogComponent | React.ComponentType | built-in Material UI Dialog | Custom UI component for the link dialog wrapper |
| toolbarConfig.lineHeight.values | Array<string\|number\|{ value, label }> | ['1', '1.15', '1.5', '1.75', '2'] | Configurable values for the line-height dropdown |
| toolbarConfig.linkConfig | object | {} | Configures link dialog fields and link-hover quick action popup |
| toolbarConfig | object | {} | Configuration object for customizing the toolbar (see below) |
Features
| Feature | Description |
|---------|-------------|
| Headings | Normal, H1, H2, H3, H4 |
| Font Family | Sans Serif, Serif, Monospace |
| Font Size | 10px–30px (2px increments), or "Default" to reset |
| Line Height | Dropdown with configurable values from toolbarConfig.lineHeight.values |
| Bold | Toggle bold |
| Italic | Toggle italic |
| Underline | Toggle underline |
| Strikethrough | Toggle strikethrough |
| Superscript | Toggle superscript |
| Subscript | Toggle subscript |
| Text Color | Color palette + custom hex + custom RGB input |
| Background Color | Color palette + custom hex + custom RGB input |
| Hyperlinks | Insert, edit, and remove links (opens in new tab) |
| Bullet List | Unordered list with style options: disc, circle, square |
| Numbered List | Ordered list with style options: decimal, upper-roman, lower-roman, lower-latin, upper-latin, lower-greek |
| Alignment | Align left, center, right, or justify |
| Indent / Outdent | Increase or decrease indentation |
| Text Direction | Toggle LTR / RTL |
| Insert Line | Inserts a horizontal rule (<hr>) |
| Undo / Redo | Toolbar buttons + keyboard shortcuts (Ctrl/Cmd+Z, Ctrl+Y, Cmd/Ctrl+Shift+Z) |
| Clear Formatting | Removes inline formatting from selected content |
| Clean Paste | Removes unsafe/noisy pasted markup while preserving common rich text |
Toolbar Configuration
All toolbar customization is done through the toolbarConfig prop.
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
lineHeight: {
values: [
"1",
{ value: "1.2", label: "Normal" },
{ value: "1.5", label: "Comfortable" },
{ value: "2", label: "Double" },
],
},
tooltipComponent: MyTooltip, // optional custom tooltip
options: {
bold: { visible: true, tooltip: "Bold (Ctrl+B)", icon: <MyBoldIcon /> },
italic: { visible: false }, // hides the italic button
// ... other buttons
},
}}
/>Hiding Buttons
Set visible: false on any button to hide it:
toolbarConfig={{
options: {
strikethrough: { visible: false },
direction: { visible: false },
}
}}Custom Tooltips
Override the tooltip text for any button:
toolbarConfig={{
options: {
bold: { tooltip: "Make Bold (Ctrl+B)" },
link: { tooltip: "Add Hyperlink" },
}
}}Custom Icons
Pass any React element as the icon for a button:
import { FaBold, FaItalic } from "react-icons/fa";
toolbarConfig={{
options: {
bold: { icon: <FaBold /> },
italic: { icon: <FaItalic /> },
}
}}For dropdown controls (heading, font, size, lineHeight), if an icon is provided then the trigger shows icon-only mode.
You can control trigger width with width in the same option object.
toolbarConfig={{
options: {
heading: { icon: <span>H</span>, width: 42 },
font: { icon: <span>F</span>, width: 40 },
size: { icon: <span>T</span>, width: 38 },
lineHeight: { icon: <span>LH</span>, width: 46 },
}
}}Custom Tooltip Component
Replace the built-in tooltip with your own component (e.g., Material UI Tooltip, Radix Tooltip, etc.). Your component must accept title and children props:
import { Tooltip as MuiTooltip } from "@mui/material";
function MyTooltip({ title, children }) {
return (
<MuiTooltip title={title} arrow placement="top">
{children}
</MuiTooltip>
);
}
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
tooltipComponent: MyTooltip,
}}
/>The custom component receives these props:
| Prop | Type | Description |
|------|------|-------------|
| title | string | The tooltip text |
| children | ReactNode | The button element to wrap |
| arrow | boolean | Always true (hint for arrow display) |
| placement | string | Always "top" |
Custom Select Component (Dropdown UI)
You can replace the built-in button dropdown controls (heading, font, size, line-height) with your own UI component.
Your component receives these props:
classNamevalueonChangeonBlurdisabledoptions(array of{ value, label, disabled?, hidden? })icon(icon configured intoolbarConfig.optionsfor that dropdown)tooltip(resolved tooltip text for that dropdown)iconOnly(boolean, true when icon-mode trigger is active)triggerWidth(resolved icon-mode width in px)children(native<option>elements for compatibility)
Example:
function MySelect({ className, value, onChange, disabled, options }) {
return (
<select
className={className}
value={value}
onChange={(e) => onChange(e)}
disabled={disabled}
>
{options.map((opt) => (
<option key={opt.value} value={opt.value} disabled={opt.disabled} hidden={opt.hidden}>
{opt.label}
</option>
))}
</select>
);
}
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
selectComponent: MySelect,
}}
/>For MUI Select, MenuItem is auto-used by default. You can still override with selectOptionComponent if needed:
import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
selectComponent: Select,
selectOptionComponent: MenuItem,
}}
/>Custom Dialog Component
You can replace the default link dialog wrapper by passing toolbarConfig.dialogComponent.
Your dialog component receives these props:
openonCloseclassNamepaperClassNametitlechildren
Example:
function MyDialog({ open, onClose, className, children }) {
if (!open) return null;
return (
<div className={className} role="dialog" aria-modal="true">
<div className="my-dialog-backdrop" onClick={onClose} />
<div className="my-dialog-panel">{children}</div>
</div>
);
}
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
dialogComponent: MyDialog,
}}
/>Link Config (Dialog + Hover Popup)
The link system supports extra fields in the insert/edit dialog:
- Text to display
- Title
- Open in new window
- Download link
It also supports a hover quick-action popup on links (enabled by default) with:
- Edit
- Copy
- Preview
- Unlink
All of this is configurable through toolbarConfig.linkConfig:
<RichTextEditor
value={html}
onChange={setHtml}
toolbarConfig={{
editor: {
placeholder: "Write your content here...",
},
linkConfig: {
dialog: {
text: {
title: "Insert Link",
urlLabel: "URL",
urlPlaceholder: "Enter URL...",
textLabel: "Text To Display",
textPlaceholder: "Displayed text",
titleLabel: "Title",
titlePlaceholder: "Tooltip title",
openInNewWindowLabel: "Open In New Window",
downloadLabel: "Download Link",
},
fields: {
text: { visible: true },
title: { visible: true },
openInNewWindow: { visible: true },
download: { visible: true },
},
actions: {
apply: {
visible: true,
icon: "✓",
label: "Apply",
tooltip: "Apply Link",
showLabel: false,
},
copy: {
visible: true,
icon: "⎘",
label: "Copy",
tooltip: "Copy Link",
showLabel: false,
},
preview: {
visible: true,
icon: "↗",
label: "Preview",
tooltip: "Open Link",
showLabel: false,
},
unlink: {
visible: true,
icon: "✕",
label: "Unlink",
tooltip: "Remove Link",
showLabel: false,
},
},
},
hoverPopup: {
enabled: true,
actions: {
edit: { visible: true, icon: "✎", label: "Edit", tooltip: "Edit Link", showLabel: false },
copy: { visible: true, icon: "⎘", label: "Copy", tooltip: "Copy Link", showLabel: false },
preview: { visible: true, icon: "↗", label: "Preview", tooltip: "Open Link", showLabel: false },
unlink: { visible: true, icon: "✕", label: "Unlink", tooltip: "Remove Link", showLabel: false },
},
},
},
}}
/>Defaults are applied automatically when any of these properties are omitted.
To disable link hover popup globally:
toolbarConfig={{
linkConfig: {
hoverPopup: {
enabled: false,
},
},
}}Toolbar Buttons Reference
These are the keys you can use inside toolbarConfig.options:
| Key | Default Tooltip | Description |
|-----|-----------------|-------------|
| bold | "Bold" | Bold button |
| italic | "Italic" | Italic button |
| underline | "Underline" | Underline button |
| strikethrough | "Strikethrough" | Strikethrough button |
| superscript | "Superscript" | Superscript button |
| subscript | "Subscript" | Subscript button |
| link | "Insert Link" | Link button |
| fontColor | "Text Color" | Font color picker button |
| bgColor | "Background Color" | Background color picker button |
| heading | "Headings" | Heading dropdown |
| font | "Font Family" | Font-family dropdown |
| size | "Font Size" | Font-size dropdown |
| lineHeight | "Line Height" | Line-height dropdown |
| bulletList | "Unordered List" | Unordered list style selector (disc/circle/square) |
| numberedList | "Ordered List" | Ordered list style selector (decimal, upper-roman, lower-roman, lower-latin, upper-latin, lower-greek) |
| alignLeft | "Align Left" | Align text left |
| alignCenter | "Align Center" | Align text center |
| alignRight | "Align Right" | Align text right |
| alignJustify | "Align Justify" | Justify text |
| decreaseIndent | "Decrease Indent" | Outdent button |
| increaseIndent | "Increase Indent" | Indent button |
| direction | "Switch to Right-to-Left" | Text direction toggle |
| insertLine | "Insert Line" | Insert horizontal rule (<hr>) |
| undo | "Undo" | Undo button |
| redo | "Redo" | Redo button |
| clearFormatting | "Clear Formatting" | Remove formatting button |
Each key accepts:
{
visible?: boolean; // default: true — set false to hide
tooltip?: string; // override default tooltip text
icon?: ReactNode; // override default icon/label
width?: number; // icon-mode trigger width in px (for heading/font/size/lineHeight)
}CSP / Nonce Support
The editor injects a <style> tag at runtime for dynamic color/font classes. If your app uses a Content Security Policy, pass a nonce:
<RichTextEditor
value={html}
onChange={setHtml}
isNonceEnabled={true}
nonceValue="abc123"
/>This adds nonce="abc123" to the injected style element.
Styling & CSS Customization
Import the required stylesheet:
import "rich-text-lite/dist/style.css";Key CSS Classes
| Class | Element |
|-------|---------|
| .rte-container | Outermost wrapper |
| .rte-toolbar | Toolbar row |
| .rte-editor | The contentEditable area |
| .rte-btn | All toolbar buttons |
| .rte-btn-active | Active/toggled toolbar button |
| .rte-select | Dropdown selects (heading, font, size) |
| .rte-color-picker | Color picker popup |
| .rte-link-popup | Link insertion popup |
Overriding Styles
You can override any class in your own CSS:
/* Change editor min-height */
.rte-editor {
min-height: 300px;
}
/* Change toolbar background */
.rte-toolbar {
background: #f5f5f5;
}
/* Change active button color */
.rte-btn-active {
background: #dbeafe;
color: #1d4ed8;
}
/* Style the dropdowns */
.rte-select {
border-radius: 4px;
font-size: 13px;
}Output Format
The onChange callback receives raw HTML from the contentEditable area. Example output:
<p><b>Hello</b> <span style="font-size: 20px">world</span></p>
<h3>A heading</h3>
<ul><li>Item one</li><li>Item two</li></ul>You can render this HTML anywhere using dangerouslySetInnerHTML or a sanitizer like DOMPurify.
Development
git clone <repo-url>
cd newgen-rich-text-editor
npm install
npm run devStarts a dev server at http://localhost:5173 with a demo page.
Build
npm run buildOutputs to dist/:
rich-text-lite.es.js— ES modulerich-text-lite.umd.js— UMD bundlestyle.css— Required styles
License
MIT
