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

@mshafiqyajid/react-rich-text

v0.4.0

Published

Headless rich text hook and styled component for React. Zero dependencies, contentEditable-based, fully typed.

Readme

@mshafiqyajid/react-rich-text

Headless rich text hook and styled component for React. Zero dependencies, contentEditable-based, fully typed.

Full docs →

Zero dependencies. Fully typed. ESM + CJS.

What's new in 0.4.0 (next)

All additive — existing props, defaults, and CSS class names are unchanged.

  • Undo/redo"undo" and "redo" added to ToolbarItem. Default keyboard shortcuts Mod+Z / Mod+Shift+Z are wired out-of-the-box. Add them to the toolbar via toolbarItems={["undo","redo",...]}.
  • renderToolbar slotrenderToolbar?: (args: RenderToolbarArgs) => ReactNode replaces the built-in toolbar when set. Receives execCommand, queryCommandState, all format flags, the current html, and disabled.
  • renderFooter slotrenderFooter?: (counts: { words: number; chars: number }) => ReactNode replaces the word-count footer row when set.
  • code toolbar item"code" added to ToolbarItem. Wraps/unwraps the selection in <code class="rrt2-inline-code"> (monospace, subtle background).
  • placeholderEachLine prop — shows a placeholder on every empty <p> / <div> block via :empty::before CSS.
  • onSelectionChange callbackonSelectionChange?: (sel: { range: Range | null; text: string }) => void fires on every selection change inside the editor. The hook also now returns getSelection: () => { range: Range | null; text: string }. JSDOM-safe.

What's new in 0.3.0

  • Keyboard shortcutsMod+B, Mod+I, Mod+U, Mod+K (link) by default. Pass shortcuts={false} to disable, or a partial map to override individual actions.
  • Inline link popover — replaces window.prompt with a real popover that includes an "Open in new tab" toggle and Edit / Remove when re-opened on an existing link. Set defaultLinkPrompt="prompt" to keep the legacy behaviour, or pass renderLinkPrompt for full custom UI.
  • Floating bubble menu — opt in via bubbleMenu to show a small toolbar over a non-empty selection. Customise items and offset via bubbleMenu={{ items: [...], offset: 8 }}.
  • Length capsmaxChars and maxWords block inserts past the cap and fire onMaxReached("chars" | "words"). Deletes always pass. The hook also exposes chars and words counts directly.
  • Auto-link — opt in via autoLink to wrap typed URLs in <a> after a space or newline. Override the pattern with autoLinkPattern.
  • Form-input parityname, id, required, invalid, error, hint, label. Renders a hidden <input> so the editor participates in <form> submissions; sets data-invalid + aria-invalid and uses the new --rrt2-border-error token when invalid.

All additive — existing props, defaults, and CSS class names are unchanged.

Install

npm install @mshafiqyajid/react-rich-text

Peer dependency: react >= 17.

Quick start

Styled (recommended)

import { RichTextStyled } from "@mshafiqyajid/react-rich-text/styled";
import "@mshafiqyajid/react-rich-text/styles.css";

<RichTextStyled
  defaultValue="<p>Hello world</p>"
  onChange={(html) => console.log(html)}
  placeholder="Start typing..."
  bubbleMenu
  autoLink
  label="Bio"
  hint="Markdown supported"
  name="bio"
/>

Headless

import { useRichText } from "@mshafiqyajid/react-rich-text";

function MyEditor() {
  const { editorProps, execCommand, isBold, isItalic, words, chars } = useRichText({
    defaultValue: "<p>Hello</p>",
    onChange: (html) => console.log(html),
    maxWords: 100,
    onMaxReached: (kind) => console.warn("hit cap:", kind),
  });

  return (
    <div>
      <button
        onMouseDown={(e) => { e.preventDefault(); execCommand("bold"); }}
        aria-pressed={isBold}
      >
        Bold
      </button>
      <div {...editorProps} />
      <small>{words} words · {chars} chars</small>
    </div>
  );
}

API

useRichText(options)

| Option | Type | Default | Description | |--------|------|---------|-------------| | value | string | — | Controlled HTML value | | defaultValue | string | "" | Uncontrolled initial HTML | | onChange | (html: string) => void | — | Called on every content change | | disabled | boolean | false | Disables editing | | readOnly | boolean | false | Makes content read-only | | sanitizePaste | boolean | true | Strip style, class, on* attrs and disallowed tags from pasted HTML. Blocks javascript: and data: URLs. Plain-text paste preserves newlines as <br>. | | allowedTags | string[] | (safe set) | Tag whitelist used by both the on-change pass and paste sanitization. Defaults cover standard formatting tags. | | transformPaste | (html) => string | — | Run after sanitizePaste. Use for custom paste transforms. | | maxChars | number | — | Hard cap on plain-text characters (deletes always pass). | | maxWords | number | — | Hard cap on whitespace-separated words. | | onMaxReached | (kind: "chars" \| "words") => void | — | Fires when an insert is blocked by a cap. | | autoLink | boolean | false | Wrap typed URLs in <a> after a space or newline. | | autoLinkPattern | RegExp | http(s) URL | Pattern used by autoLink. | | shortcuts | ShortcutMap \| false | — | Headless shortcut spec (the styled component sets defaults). | | onShortcut | (action, event) => void | — | Fires when a shortcut matches; useful for custom actions. |

Returns: editorProps, execCommand(command, value?), queryCommandState(command), isBold, isItalic, isUnderline, isStrikethrough, html, chars, words.

RichTextStyled

All useRichText options plus:

| Prop | Type | Default | Description | |------|------|---------|-------------| | size | "sm" \| "md" \| "lg" | "md" | Editor size preset | | tone | "neutral" \| "primary" | "neutral" | Color tone | | placeholder | string | — | Placeholder text | | minHeight | string | "120px" | Min height of editor area | | maxHeight | string | — | Max height (enables scroll) | | showToolbar | boolean | true | Show/hide the toolbar | | toolbarItems | ToolbarItem[] | all items | Which toolbar buttons to show | | wordCount | boolean | false | Show word/character count footer | | spellCheck | boolean | true | Enable spell check | | shortcuts | boolean \| ShortcutMap | true | Built-in shortcuts (Mod+B/I/U/K). false to disable; partial map to override. | | defaultLinkPrompt | "popover" \| "prompt" | "popover" | Inline popover (with new-tab toggle and Edit / Remove on existing links) or legacy window.prompt. | | renderLinkPrompt | (args) => ReactNode | — | Render-prop override for the link prompt UI. | | bubbleMenu | boolean \| { items?: ToolbarItem[]; offset?: number } | false | Floating toolbar over non-empty selections. | | name | string | — | Hidden <input name> so the editor submits in a <form>. | | id | string | auto | id for the editor; wires up the label. | | required | boolean | false | Adds an asterisk to the label and aria-required. | | invalid | boolean | false | Sets data-invalid + aria-invalid; uses --rrt2-border-error. | | error | ReactNode | — | Error message below the editor. Implies invalid. | | hint | ReactNode | — | Hint message below the editor when no error. | | label | ReactNode | — | Label rendered above the editor. |

License

MIT