npm package: @shahji/text-editor
Description: A production-ready, scalable rich text editor component with absolute positioning control
Author: Mahtab Shah
Latest version: 1.0.4


# @shahji/text-editor

![npm version](https://img.shields.io/npm/v/@shahji/text-editor)
![npm bundle size](https://img.shields.io/bundlephobia/minzip/@shahji/text-editor)
![npm downloads](https://img.shields.io/npm/dm/@shahji/text-editor)
![license](https://img.shields.io/npm/l/@shahji/text-editor)
![React](https://img.shields.io/badge/React-%E2%89%A516.8-blue)
![TypeScript Ready](https://img.shields.io/badge/TypeScript-Ready-blue)
![GitHub stars](https://img.shields.io/github/stars/shahji/text-editor)
![GitHub issues](https://img.shields.io/github/issues/shahji/text-editor)

A production-ready, scalable rich text editor component for React with comprehensive error handling, accessibility support, and extensible architecture.

## 📸 Demo

<img 
  src="https://res.cloudinary.com/dft5cl5ra/image/upload/v1769587374/Screenshot_2026-01-28_133241_bnytzp.png"
  width="400"
/>

## ✨ Features

| Feature | Description |
|---------|-------------|
| 🎯 **Production Ready** | Comprehensive error handling and graceful degradation |
| ♿ **Accessibility First** | Full ARIA support and keyboard navigation |
| 🚀 **High Performance** | Optimized with React.memo and useCallback |
| 🔧 **Extensible API** | Complete programmatic control via ref |
| 📱 **Mobile Friendly** | Touch and gesture support |
| 🎨 **Customizable** | Full style control with CSS-in-JS |
| 📝 **Rich Text** | Character-level styling and formatting |
| 🛡️ **Safe** | XSS protection and secure content handling |
| 📦 **Zero Dependencies** | Only React and PropTypes required |

## 📦 Installation

```bash
# Using npm
npm install @shahji/text-editor

# Using yarn
yarn add @shahji/text-editor

# Using pnpm
pnpm add @shahji/text-editor
```


## 🚀 Quick Start
```jsx
import "bootstrap/dist/css/bootstrap.min.css";
import React, { useRef } from 'react';
import { EditorFrame } from '@shahji/text-editor';

function SimpleEditor() {
  const editorRef = useRef();

  return (
    <EditorFrame
      ref={editorRef}
      placeholder="Start typing here..."
      style={{
        border: '2px solid #007bff',
        borderRadius: '8px',
        padding: '16px',
        minHeight: '200px',
        fontSize: '16px'
      }}
    />
  );
}

```


1. Basic Editor with Styling

```jsx
import "bootstrap/dist/css/bootstrap.min.css";
import React, { useRef , useState} from 'react';
import { EditorFrame } from '@shahji/text-editor';

function BasicEditor() {
  const editorRef = useRef();
  const [_style, set_style] = useState({fontSize: '16px', color: '#212020' });
  
  const handleBoldClick = () => {
    if (editorRef.current) {
      set_style((prev)=>({
        ...prev, ["color"]: "red", ["fontWeight"]:"bold",
      }))
    }
  };
  
  return (
    <div>
      <EditorFrame
        ref={editorRef}
        curr_style={_style}
        style={{ border: "1px solid" }}
        placeholder="Type your content here..."
      />
      <button onClick={handleBoldClick}>Click for Bold & Red</button>

    </div>
  );
}
```


2. Simple and Complete exmaple, easy to understand

```jsx

import "bootstrap/dist/css/bootstrap.min.css";
import React, { useRef, useState } from "react";
import { EditorFrame } from '@shahji/text-editor';

function App() {
  const editorRef = useRef();
  const [curr_style, setCurr_style] = useState({ color: "#080808" });
  const activeStyle = { background: "#1759f1", color: "#f3f3f3" };

  const _toggle_style = (key, value) => {
    setCurr_style((prev) => {
      if (prev[key] == value) {
        const newObj = { ...prev };
        delete newObj[key];
        return newObj;
      }

      return { ...prev, [key]: value };
    });

    editorRef.current.focus();

    return curr_style;
  };

  const isActive = (key, value) => curr_style[key] === value;

  const handleError = (error) => {
    console.error("Editor error:", error);
  };

  const props = {
    props: [
      { key: "fontStyle", value: "italic", label: "I" },
      { key: "fontWeight", value: "bold", label: "B" },
      { key: "textDecoration", value: "underline", label: "U" },
      { key: "color", value: "red", label: "Red" },
      { key: "backgroundColor", value: "#f2ff00", label: "Yellow" },
    ],
  };

  return (
    <div>
      <button onClick={() => editorRef.current?.clear()}>Clear Editor</button>

      <div className="editor-toolbar mb-2">
        {props.props.map((item) => (
          <button
            key={item.key}
            className="editor-btn"
            onClick={() => _toggle_style(item.key, item.value)}
            style={isActive(item.key, item.value) ? activeStyle : {}}
          >
            {item.label}
          </button>
        ))}
      </div>

      <EditorFrame
        ref={editorRef}
        curr_style={curr_style}
        onError={handleError}
        placeholder="Start typing..."
        autoFocus={true}
        style={{
          border: "2px solid #007bff",
          borderRadius: "4px",
          padding: "12px",
          minHeight: "200px",
        }}
      />
    </div>
  );
}
```


| Prop                | Type                          | Default                                                                                                       | Description                                       |
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `style`             | `object`                      | `{ border: "1px solid #455156", padding: "4px", color: "#0a0a0a", background: "#ffffff", caretColor: "red" }` | CSS styles for the editor container               |
| `curr_style`        | `object`                      | `{}`                                                                                                          | Current text style applied to newly inserted text |
| `className`         | `string`                      | `""`                                                                                                          | Additional CSS class names for custom styling     |
| `placeholder`       | `string`                      | `"Type here..."`                                                                                              | Placeholder text when the editor is empty     
| `autoFocus`         | `boolean`                     | `false`                                                                                                       | Automatically focuses the editor on mount         |
| `spellCheck`        | `boolean`                     | `true`                                                                                                        | Enables browser spell checking                    |
| `ariaLabel`         | `string`                      | `"Rich text editor"`                                                                                          | ARIA label for accessibility                      |
| `ariaDescription`   | `string`                      | `"Type or paste text with formatting"`                                                                        | ARIA description for assistive technologies       |
| `onContentChange`   | `(html: string) => void`      | `undefined`                                                                                                   | Callback triggered when content changes           |
| `onSelectionChange` | `(selection: object) => void` | `undefined`                                                                                                   | Callback triggered on text selection changes      |
| `onError`           | `(error: object) => void`     | `undefined`                                                                                                   | Callback for handling runtime errors              |




## Ref Methods
Access these methods via the component ref:

```jsx
const editorRef = useRef();

// Access methods
editorRef.current.insertText("Hello World");
```

| Method             | Parameters      | Returns   | Description                                       |
| ------------------ | --------------- | --------- | ------------------------------------------------- |
| `insertText`       | `text: string`  | `void`    | Inserts text at the current cursor position       |
| `getContent`       | `–`             | `string`  | Returns the editor’s HTML content                 |
| `getSelectionInfo` | `–`             | `object`  | Retrieves information about the current selection |
| `focus`            | `–`             | `void`    | Brings focus to the editor                        |
| `blur`             | `–`             | `void`    | Removes focus from the editor                     |
| `clear`            | `–`             | `void`    | Clears all content from the editor                |



## 🎨 Advanced Examples

## 1. Custom Styling Per Character

```jsx
import "bootstrap/dist/css/bootstrap.min.css";
import React, { useRef, useState } from "react";
import { EditorFrame } from '@shahji/text-editor';

function GradientTextEditor() {
  const editorRef = useRef();
  const colors = ["#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff"];
  const [curr_style, setCurr_style] = useState({ color: "#080808" });

  const insertRainbowText = () => {
    const text = "Rainbow Text Effect";
    let delay = 0;

    text.split("").forEach((char, index) => {
      setTimeout(() => {
        setCurr_style((prev) => ({
          ...prev,
          ["color"]: colors[Math.floor(Math.random() * 4)],
        }));
        editorRef.current.insertText(char);
      }, delay);
      delay += 50;
    });
  };

  return (
    <>
      <EditorFrame ref={editorRef} curr_style={curr_style} />
      <button onClick={insertRainbowText}>Insert Rainbow Text</button>
    </>
  );
}
```


## 🔒 Security Features
XSS Protection: Automatic sanitization of pasted content

Safe Clipboard: Uses secure clipboard API with fallbacks

Input Validation: All inputs are validated before processing

Error Boundaries: Graceful error handling without crashes



## Accessibility
Screen Reader Support: Full ARIA attributes

Keyboard Navigation: Tab navigation, arrow keys for cursor

Focus Management: Proper focus trapping and management

High Contrast: Customizable for accessibility needs




## ⚡ Performance Optimizations
Memoization: Components and callbacks are memoized

Virtual DOM: Efficient updates with React reconciliation

Debounced Updates: Content change callbacks are optimized

Memory Management: Proper cleanup of event listeners

Lazy Loading: Large features load on demand


## Debugging
Development Mode Features

```jsx
<EditorFrame
  ref={editorRef}
  onError={(error) => {
    // Log to your error tracking service
    console.error('Editor Error:', error);
    sentry.captureException(error);
  }}
  // Visual debug indicators in development
  style={{
    ...(process.env.NODE_ENV === 'development' && {
      outline: '1px dashed #f0f'
    })
  }}
/>
```

## 🛠 Common Issues & Solutions

| Issue                     | Solution                                                                                                                 |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Cursor jumps to beginning | Ensure the editor component is not being re-created on every render. Use `useRef` to maintain a stable editor reference. |
| Styles not applying       | Verify that the `curr_style` prop is updating correctly and that the CSS properties used are supported by the browser.   |
| Paste not working         | Ensure clipboard permissions are granted and confirm browser compatibility with clipboard APIs.                          |
| Text not appearing        | Check that the `readOnly` prop is set to `false` and that the editor currently has focus.                                |



📝 License
MIT © Mahtab Shah

## Support
Documentation: upcoming...

Issues: GitHub Issues upcoming...

Email: mahtab.shah916@gmail.com