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

@cannyminds/rich-text-editor

v0.3.0

Published

A React Rich Text Editor with multi-user track changes, complete JSON history export, and collaborative editing support. Built on Tiptap.

Readme

@cannyminds/rich-text-editor

A powerful React Rich Text Editor component built with Tiptap, featuring multi-user track changes with complete JSON history export.

npm version License: MIT

✨ Features

  • 🎨 Multi-User Track Changes - Real-time collaborative editing with user-specific highlights
  • 📋 Complete History in JSON - Export all changes with user attribution and timestamps
  • 🛠️ Headless Architecture - Full styling control, works with any design system
  • ⚛️ React 16.8+ - Modern hooks-based API with TypeScript support
  • 🔌 Extensible - Built on Tiptap, use 100+ community extensions
  • 📝 Rich Formatting - Tables, images, links, code blocks, and more

📦 Installation

npm

npm install @cannyminds/rich-text-editor

pnpm

pnpm add @cannyminds/rich-text-editor

yarn

yarn add @cannyminds/rich-text-editor

bun

bun add @cannyminds/rich-text-editor

Local Development (Monorepo / Workspaces)

If you're developing locally within a monorepo or want to link the package during development:

pnpm workspaces:

// In your consuming package's package.json
{
  "dependencies": {
    "@cannyminds/rich-text-editor": "workspace:*"
  }
}

npm/yarn link:

# In the rich-text-editor package directory
npm link

# In your consuming project
npm link @cannyminds/rich-text-editor

File reference:

// In your consuming package's package.json
{
  "dependencies": {
    "@cannyminds/rich-text-editor": "file:../packages/rich-text-editor"
  }
}

🚀 Quick Start

Basic Usage

import { RichTextEditor } from '@cannyminds/rich-text-editor';
import '@cannyminds/rich-text-editor/styles.css';

function App() {
  const [content, setContent] = useState('<p>Hello World!</p>');

  return (
    <RichTextEditor
      content={content}
      onUpdate={(html) => setContent(html)}
      placeholder="Start writing..."
      showToolbar={true}
      showCharacterCount={true}
    />
  );
}

With Track Changes

import { 
  RichTextEditor, 
  TrackChanges,
  type TrackChangesUser 
} from '@cannyminds/rich-text-editor';
import '@cannyminds/rich-text-editor/styles.css';

function CollaborativeEditor() {
  const [changes, setChanges] = useState<TrackChange[]>([]);
  
  const currentUser: TrackChangesUser = {
    id: 'user-1',
    name: 'John Doe',
    color: '#3B82F6'  // Blue highlights
  };

  const handleTrackChange = (change: TrackChange) => {
    // Each change is delivered as JSON
    setChanges(prev => [...prev, {
      id: crypto.randomUUID(),
      ...change,
    }]);
  };

  return (
    <RichTextEditor
      content=""
      extensions={[
        TrackChanges.configure({
          enabled: true,
          currentUser,
          onTrackChange: handleTrackChange,
        })
      ]}
    />
  );
}

📊 Track Changes JSON Format

Every change is captured in a structured JSON format, perfect for auditing, version control, and collaborative workflows.

TrackChange Interface

interface TrackChange {
  userId: string;                    // User identifier
  userName: string;                  // Display name
  userColor: string;                 // Hex color for highlighting
  text: string;                      // Changed text content
  from: number;                      // Start position in document
  to: number;                        // End position in document
  type: 'insert' | 'delete';         // Type of change
}

TextChange (Extended Format)

For complete history tracking with persistence:

interface TextChange {
  id: string;                        // Unique change identifier
  userId: string;                    // User ID
  userName: string;                  // User display name
  userColor: string;                 // Hex color (#3B82F6)
  timestamp: number;                 // Unix timestamp (ms)
  startOffset: number;               // Document start position
  endOffset: number;                 // Document end position
  oldText: string;                   // Text before change
  newText: string;                   // Text after change
  type: 'insert' | 'delete' | 'replace';
}

Example JSON Output

[
  {
    "id": "change-1705678901234",
    "userId": "user-1",
    "userName": "John Doe",
    "userColor": "#3B82F6",
    "timestamp": 1705678901234,
    "startOffset": 0,
    "endOffset": 12,
    "oldText": "",
    "newText": "Hello World!",
    "type": "insert"
  },
  {
    "id": "change-1705678905678",
    "userId": "user-2", 
    "userName": "Jane Smith",
    "userColor": "#10B981",
    "timestamp": 1705678905678,
    "startOffset": 6,
    "endOffset": 11,
    "oldText": "World",
    "newText": "",
    "type": "delete"
  }
]

🎨 Multi-User Highlighting

Each user gets a unique color for their changes:

const users: TrackChangesUser[] = [
  { id: 'user-1', name: 'John Doe', color: '#3B82F6' },     // Blue
  { id: 'user-2', name: 'Jane Smith', color: '#10B981' },   // Green
  { id: 'user-3', name: 'Bob Wilson', color: '#F59E0B' },   // Orange
  { id: 'user-4', name: 'Alice Brown', color: '#8B5CF6' },  // Purple
];

Visual Indicators:

  • Insertions: Background highlight + colored underline
  • Deletions: Red strikethrough with light red background
  • Tooltips: Hover to see "Added by [User] • [Date/Time]"

🔧 API Reference

RichTextEditor Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | content | string | '' | Initial HTML content | | onUpdate | (html: string) => void | - | Callback on content change | | onBlur | (html: string) => void | - | Callback on blur | | editable | boolean | true | Enable/disable editing | | placeholder | string | 'Start writing...' | Placeholder text | | extensions | Extension[] | [] | Additional Tiptap extensions | | showToolbar | boolean | true | Show/hide toolbar | | showCharacterCount | boolean | false | Show character count | | minHeight | number \| string | 200 | Minimum editor height | | maxHeight | number \| string | - | Maximum editor height | | autoFocus | boolean | false | Auto-focus on mount | | className | string | '' | Custom container class |

TrackChanges Extension Options

TrackChanges.configure({
  enabled: boolean;              // Enable/disable tracking
  currentUser: TrackChangesUser; // Current user info
  onTrackChange: (change) => {}; // Change callback
})

Editor Commands

// Enable/disable tracking
editor.commands.enableTrackChanges();
editor.commands.disableTrackChanges();

// Set current user
editor.commands.setTrackChangesUser({
  id: 'user-1',
  name: 'John Doe',
  color: '#3B82F6'
});

🎯 Advanced Usage

Headless Mode (Custom UI)

import { useEditor, EditorContent, StarterKit } from '@cannyminds/rich-text-editor';
import '@cannyminds/rich-text-editor/styles.css';

function CustomEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Custom UI</p>',
  });

  return (
    <div>
      <button onClick={() => editor?.chain().focus().toggleBold().run()}>
        Bold
      </button>
      {/* 
        IMPORTANT: Wrap EditorContent in 'rte-content' to apply library styles 
        (tables, lists, blockquotes, etc.) 
      */}
      <div className="rte-content">
        <EditorContent editor={editor} />
      </div>
    </div>
  );
}

Export History as JSON

// Get all changes from your state management
const exportHistory = () => {
  const historyJson = JSON.stringify(changes, null, 2);
  
  // Download as file
  const blob = new Blob([historyJson], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'document-history.json';
  a.click();
};

Toggle Highlight Visibility

Use CSS to show/hide track change highlights:

/* Hide all highlights */
.hide-highlights .track-change-highlight {
  background: transparent !important;
  border-bottom: none !important;
}

/* Hide deletions */
.hide-highlights .track-change-delete {
  display: none;
}

📚 Exports

// Components
export { RichTextEditor } from '@cannyminds/rich-text-editor';

// Extensions
export { TrackChanges, trackChangesPluginKey } from '@cannyminds/rich-text-editor';
export { ResizableImage } from '@cannyminds/rich-text-editor';

// Tiptap Re-exports
export { useEditor, EditorContent } from '@cannyminds/rich-text-editor';
export { StarterKit, Table, Link, Image, Highlight } from '@cannyminds/rich-text-editor';

// Types
export type { 
  TrackChangesUser, 
  TrackChangesOptions,
  RichTextEditorProps,
  Editor 
} from '@cannyminds/rich-text-editor';

🎨 Styling

Import the default styles:

import '@cannyminds/rich-text-editor/styles.css';

Or use the dist path:

import '@cannyminds/rich-text-editor/dist/styles.css';

CSS Variables (Customization)

:root {
  --rte-border-color: #e2e8f0;
  --rte-toolbar-bg: #f8fafc;
  --rte-content-bg: #ffffff;
  --rte-focus-ring: #3b82f6;
}

📄 License

MIT © CannyMinds Dev Team


🔗 Links