npm package: @npdevind/n-editor
Description: Simple React text editor using TipTap
Author: npdevind
Latest version: 1.0.3

# @npdevind/n-editor

A **production-ready**, fully-featured rich text editor React component built on top of [TipTap](https://tiptap.dev/). Drop it into any React project with a single import.

![NP Text Editor Demo](https://raw.githubusercontent.com/npdevin-npm-package/n-editer/main/assets/demo.png)

---

## ✨ Features

- 🖊️ **Rich Text Formatting** — Bold, Italic, Underline, text color picker
- 🔤 **Font Family & Size** — Custom dropdown selectors for Font Family and Font Size
- 📐 **Text Alignment** — Align Left, Center, Right, and Justify via a smart dropdown
- 🔗 **Link Insertion** — Insert and edit hyperlinks with a simple prompt
- 🖼️ **Local Image Upload** — Click the image button to pick a photo from your local file system — no URL needed!
- 📊 **Configurable Tables** — Open the table popover, set your Row and Column counts, and insert instantly
- 📝 **Bullet Lists** — Toggle bullet lists with a single click
- 🎨 **Clean Modern UI** — Subtle borders, rounded corners, hover states — a premium SaaS-quality look out of the box
- 🔧 **Fully Customizable** — Override extensions, styles, and even replace the toolbar with your own render prop
- 🔖 **TypeScript First** — Complete type definitions included
- ⚡ **Lightweight** — Peer depends on React; doesn't bundle duplicate React/ReactDOM into your app

---

## 📦 Installation

```bash
npm install @npdevind/n-editor
```

> **Peer Dependency:** This package requires `react >= 18` to be installed in your project.

---

## 🚀 Quick Start

```tsx
import { useState } from "react";
import { TextEditor } from "@npdevind/n-editor";
import "@npdevind/n-editor/style.css"; // Required for styles

export default function App() {
  const [content, setContent] = useState("<p>Start typing...</p>");

  return <TextEditor value={content} onChange={setContent} />;
}
```

> ⚠️ **Don't forget** to import `'@npdevind/n-editor/style.css'` in your app. Without it, the editor will have no styles.

---

## 🎛️ Props

| Prop               | Type                            | Default | Description                                             |
| ------------------ | ------------------------------- | ------- | ------------------------------------------------------- |
| `value`            | `string`                        | `""`    | The initial HTML content of the editor                  |
| `onChange`         | `(html: string) => void`        | —       | Called every time the content changes, returns raw HTML |
| `className`        | `string`                        | `""`    | CSS class applied to the outer wrapper `div`            |
| `editorClassName`  | `string`                        | `""`    | CSS class applied to the inner content area             |
| `toolbarClassName` | `string`                        | `""`    | CSS class applied to the toolbar                        |
| `readOnly`         | `boolean`                       | `false` | Disables editing and hides the toolbar                  |
| `showToolbar`      | `boolean`                       | `true`  | Show or hide the default toolbar                        |
| `renderToolbar`    | `(editor: Editor) => ReactNode` | —       | Render prop to provide a completely custom toolbar      |
| `extensions`       | `Extension[]`                   | —       | Override the default TipTap extensions entirely         |
| `editorProps`      | `Record<string, any>`           | —       | Extra props passed to the TipTap `editorProps` option   |

---

## 📖 Examples

### Read-Only Mode

```tsx
<TextEditor
  value="<p>This content is <strong>read-only</strong>.</p>"
  readOnly={true}
/>
```

### Custom Styling

```tsx
<TextEditor
  value={content}
  onChange={setContent}
  className="my-outer-wrapper"
  editorClassName="my-content-area"
/>
```

```css
/* In your CSS file */
.my-outer-wrapper {
  max-width: 800px;
  margin: 0 auto;
}

.my-content-area {
  min-height: 400px;
  font-family: "Georgia", serif;
}
```

### Custom Toolbar with `renderToolbar`

You can replace the entire toolbar using the `renderToolbar` render prop. It receives the TipTap `Editor` instance directly:

```tsx
import { TextEditor } from "@npdevind/n-editor";
import "@npdevind/n-editor/style.css";

<TextEditor
  value={content}
  onChange={setContent}
  renderToolbar={(editor) => (
    <div className="my-toolbar">
      <button onClick={() => editor.chain().focus().toggleBold().run()}>
        Bold
      </button>
      <button onClick={() => editor.chain().focus().toggleItalic().run()}>
        Italic
      </button>
    </div>
  )}
/>;
```

### Programmatic Control via `ref`

```tsx
import { useRef } from "react";
import { TextEditor, TextEditorRef } from "@npdevind/n-editor";
import "@npdevind/n-editor/style.css";

export default function App() {
  const editorRef = useRef<TextEditorRef>(null);

  const clearContent = () => {
    editorRef.current?.editor?.commands.clearContent();
  };

  const getHTML = () => {
    const html = editorRef.current?.editor?.getHTML();
    console.log(html);
  };

  return (
    <>
      <TextEditor ref={editorRef} value="<p>Hello world</p>" />
      <button onClick={clearContent}>Clear</button>
      <button onClick={getHTML}>Log HTML</button>
    </>
  );
}
```

---

## 🎨 Styling & Theming

All elements use prefixed CSS classes (`.np-*`) to avoid any conflicts with your existing styles:

| Class                    | Target                                         |
| ------------------------ | ---------------------------------------------- |
| `.np-editor-wrapper`     | The outermost container div                    |
| `.np-editor-toolbar`     | The toolbar row                                |
| `.np-editor-content`     | The editable content area                      |
| `.np-toolbar-btn`        | Individual toolbar icon buttons                |
| `.np-toolbar-btn.active` | Active state on toolbar buttons                |
| `.np-dropdown`           | Dropdown containers (Font, Size, Align, Table) |
| `.np-divider`            | The thin vertical dividers in the toolbar      |

You can easily override any of these in your own CSS:

```css
/* Make the editor border blue */
.np-editor-wrapper {
  border-color: #3b82f6;
}

/* Make active buttons use your brand color */
.np-toolbar-btn.active {
  background-color: #dbeafe;
  color: #1d4ed8;
}
```

---

## 🛠️ Included TipTap Extensions

The following TipTap extensions are **bundled by default**:

- `StarterKit` (Headings, Paragraphs, Bold, Italic, Lists, Blockquote, Code, History)
- `Underline`
- `TextStyle` + `Color` (inline text coloring)
- `TextAlign`
- `Link`
- `Image`
- `FontFamily`
- `Table` + `TableRow` + `TableHeader` + `TableCell`
- Custom `FontSize` extension (inline CSS size injection)

---

## 📤 Output Format

The `onChange` callback returns **raw HTML** (standard TipTap HTML output). You can store it in your database as-is and render it back via `value` prop or with `dangerouslySetInnerHTML`.

```tsx
// Rendering saved HTML content safely
<div dangerouslySetInnerHTML={{ __html: savedContent }} />
```

---

## 🔗 Links

- **NPM Package:** [https://www.npmjs.com/package/@npdevind/n-editor](https://www.npmjs.com/package/@npdevind/n-editor)
- **Built with:** [TipTap](https://tiptap.dev/) · [Lucide React](https://lucide.dev/) · [React](https://react.dev/)

---

## 📄 License

MIT © [npdevind](https://github.com/npdevind)