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

@myexamly/word-editor

v0.0.5

Published

A feature-rich Word-like rich text editor built with Slate.js, React and Tailwind CSS

Readme

@myexamly/word-editor

A feature-rich, Word-like rich text editor for React — built with Slate.js and Tailwind CSS.

Features

  • 📝 Full inline formatting: bold, italic, underline, strikethrough, code, superscript, subscript
  • 🎨 Text & background colour, font family, font size
  • 📐 Headings (H1–H6), blockquote, code block, divider
  • 📋 Bullet list, numbered list, checklist (task list)
  • 📏 Line spacing & text alignment
  • 🔗 Inline links (click to open), resizable images
  • 📊 Full-featured tables (merge cells, borders, banded rows/cols)
  • 🔍 Find & Replace (Ctrl/⌘+F)
  • 😀 Emoji picker & special characters
  • 🌐 Translation via LibreTranslate (configurable endpoint + API key)
  • ✅ Grammar check via LanguageTool (configurable endpoint, wavy underlines, inline suggestions)
  • 🖨️ Print & Export (HTML / plain text / JSON / Markdown / RTF / PDF)
  • 🔎 Zoom (50%–200%), word & character count
  • ↩️ Full undo/redo, right-click context menu, smart paste

Installation

npm install @myexamly/word-editor
# peer dependencies
npm install react react-dom slate slate-react slate-history

Basic Usage

import { RichTextEditor } from "@myexamly/word-editor";
import "@myexamly/word-editor/style.css";

function App() {
  return (
    <RichTextEditor
      placeholder="Start writing..."
      className="h-screen"
      onChange={(value) => console.log(value)}
    />
  );
}

Props

| Prop | Type | Default | Description | | ----------------- | ------------------------------- | --------------------------------------- | ------------------------------------------------------------------ | | placeholder | string | 'Start typing...' | Placeholder text shown in the empty editor | | initialValue | Descendant[] | single empty paragraph | Initial Slate document value | | onChange | (value: Descendant[]) => void | — | Fired on every document change | | className | string | '' | CSS class on the root wrapper element | | features | ToolbarFeatures | all enabled | Fine-grained control over which toolbar buttons are shown | | header | HeaderConfig | shown with defaults | Control the built-in header bar | | translateApiKey | string | '' | API key for LibreTranslate (required when translate feature is on) | | translateApiUrl | string | https://libretranslate.com/translate | Override the LibreTranslate endpoint (e.g. self-hosted) | | grammarApiUrl | string | https://api.languagetool.org/v2/check | Override the LanguageTool endpoint (e.g. self-hosted) |


ToolbarFeatures

Set any key to false to hide that toolbar button/group. Omit or set true to show it.

<RichTextEditor
  features={{
    translate: true, // show LibreTranslate button
    grammar: true, // show grammar check button
    emoji: false, // hide emoji picker
    export: false, // hide export menu
    table: true,
  }}
  translateApiKey="your-libretranslate-key"
/>

| Key | Default | Description | | ----------------- | ------- | ------------------------------------------------------- | | undoRedo | true | Undo / Redo buttons | | blockType | true | Block type selector (paragraph / headings / etc.) | | fontFamily | true | Font family picker | | fontSize | true | Font size picker | | textColor | true | Text colour | | highlight | true | Text background highlight | | pasteMode | true | Paste-mode selector | | findReplace | true | Find & Replace (Ctrl/⌘+F) | | export | true | Export menu (HTML / text / JSON / Markdown / RTF / PDF) | | bold | true | Bold | | italic | true | Italic | | underline | true | Underline | | strikethrough | true | Strikethrough | | inlineCode | true | Inline code | | superSubscript | true | Superscript / Subscript | | quickHighlight | true | Quick colour-highlight buttons | | clearFormatting | true | Clear all formatting | | formatPainter | true | Format painter | | align | true | Text alignment | | indent | true | Indent / Outdent | | lists | true | Bullet & numbered lists | | blockquote | true | Blockquote | | lineSpacing | true | Line spacing picker | | emoji | true | Emoji picker | | specialChars | true | Special characters picker | | link | true | Insert link | | image | true | Insert image | | table | true | Insert table | | divider | true | Insert horizontal divider | | translate | true | LibreTranslate integration | | grammar | true | LanguageTool grammar check |


HeaderConfig

<RichTextEditor
  header={{
    show: true,
    showLogo: true,
    logo: <img src="/my-logo.png" width={32} />, // custom logo
    showTitle: true,
    title: "My Document",
    showDescription: false,
    rightSlot: <button>Save</button>, // rendered on the right
  }}
/>

| Key | Type | Default | Description | | ----------------- | ----------- | ----------------------------- | ---------------------------------------- | | show | boolean | true | Show / hide the entire header bar | | showLogo | boolean | true | Show / hide the logo | | logo | ReactNode | default SVG icon | Custom logo element | | showTitle | boolean | true | Show / hide the title | | title | string | 'Examly Word Editor' | Title text | | showDescription | boolean | true | Show / hide the subtitle | | description | string | 'Rich Text Document Editor' | Subtitle text | | rightSlot | ReactNode | — | Slot rendered on the right of the header |


Translation (LibreTranslate)

The translate button opens a modal that:

  • Pre-fills with the currently selected text
  • Lets the user pick a target language from a searchable dropdown
  • Shows the primary translation + up to 3 alternatives
  • Applies the chosen translation back into the editor selection
// Public LibreTranslate cloud (requires API key)
<RichTextEditor
  features={{ translate: true }}
  translateApiKey="your-api-key"
/>

// Self-hosted LibreTranslate instance (no key needed)
<RichTextEditor
  features={{ translate: true }}
  translateApiUrl="https://translate.myserver.com/translate"
/>

Grammar Check (LanguageTool)

When enabled, the editor automatically sends document text to LanguageTool after the user stops typing (400 ms debounce). Errors are shown with a red wavy underline. Click any underlined word to see suggestions.

// Public LanguageTool API (free, no key required)
<RichTextEditor features={{ grammar: true }} />

// Self-hosted LanguageTool instance
<RichTextEditor
  features={{ grammar: true }}
  grammarApiUrl="https://lt.myserver.com/v2/check"
/>

// Disable grammar check entirely
<RichTextEditor features={{ grammar: false }} />

API Endpoint Constants

The default endpoint URLs are exported so you can inspect or log them:

import {
  DEFAULT_GRAMMAR_API_URL, // 'https://api.languagetool.org/v2/check'
  DEFAULT_TRANSLATE_API_URL, // 'https://libretranslate.com/translate'
  GRAMMAR_DEBOUNCE_MS, // 400
  GRAMMAR_CACHE_MAX, // 20
} from "@myexamly/word-editor";

Controlled Value

import { useState } from "react";
import { RichTextEditor } from "@myexamly/word-editor";
import type { Descendant } from "@myexamly/word-editor";
import "@myexamly/word-editor/style.css";

const INITIAL: Descendant[] = [
  { type: "heading-one", children: [{ text: "Hello World" }] },
  { type: "paragraph", children: [{ text: "Start editing..." }] },
];

function App() {
  const [value, setValue] = useState<Descendant[]>(INITIAL);
  return (
    <RichTextEditor
      initialValue={value}
      onChange={setValue}
      className="h-screen"
    />
  );
}

Using Utility Functions

import { useSlate } from "slate-react";
import {
  toggleMark,
  insertTable,
  toggleChecklistItem,
} from "@myexamly/word-editor";

function MyButton() {
  const editor = useSlate();
  return <button onClick={() => toggleMark(editor, "bold")}>Bold</button>;
}

Peer Dependencies

| Package | Version | | --------------- | --------- | | react | ≥ 18 | | react-dom | ≥ 18 | | slate | ≥ 0.100.0 | | slate-react | ≥ 0.100.0 | | slate-history | ≥ 0.100.0 |


Keyboard Shortcuts

| Shortcut | Action | | -------------------- | --------------------- | | Ctrl/⌘ + B | Bold | | Ctrl/⌘ + I | Italic | | Ctrl/⌘ + U | Underline | | Ctrl/⌘ + \`` | Inline code | | Ctrl/⌘ + Shift + X| Strikethrough | |Ctrl/⌘ + Shift + 7| Numbered list | |Ctrl/⌘ + Shift + 8| Bulleted list | |Ctrl/⌘ + Shift + B| Blockquote | |Ctrl/⌘ + F | Find & Replace | |Ctrl/⌘ + Z / Y | Undo / Redo | |Tabin table | Move to next cell | |Shift+Tab` in table | Move to previous cell |


License

MIT © Examly