npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rich-text-lite

v4.0.0

Published

A lightweight, customizable rich text editor React component

Readme

rich-text-lite

A lightweight, zero-dependency (besides React) rich text editor React component with a fully customizable toolbar.

Table of Contents


Installation

npm install rich-text-lite

Quick 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:

  • className
  • value
  • onChange
  • onBlur
  • disabled
  • options (array of { value, label, disabled?, hidden? })
  • icon (icon configured in toolbarConfig.options for 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:

  • open
  • onClose
  • className
  • paperClassName
  • title
  • children

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 dev

Starts a dev server at http://localhost:5173 with a demo page.

Build

npm run build

Outputs to dist/:

  • rich-text-lite.es.js — ES module
  • rich-text-lite.umd.js — UMD bundle
  • style.css — Required styles

License

MIT