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 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-richtext-tiptap

v1.0.2

Published

A fully customizable and extensible React editor built on TipTap with drag-and-drop images, complete TypeScript support, and modern UI components

Readme

React TipTap Editor

A comprehensive, fully customizable React editor library built on TipTap with advanced drag-and-drop image functionality, complete TypeScript support, and modern UI components.

React TipTap Editor Demo

🚀 Features

  • 🎯 Drag & Drop Images - Upload and position images anywhere in your content with smart text wrapping
  • 🔧 Fully Extensible - Override any extension, component, or behavior without touching library code
  • 📝 Rich Text Editing - Complete formatting tools including tables, lists, code blocks, and more
  • 🎨 Customizable UI - Styled with Tailwind CSS but completely themeable and overridable
  • 📱 Responsive Design - Mobile-first design with touch-friendly controls
  • ⚡ Performance Optimized - Built for fast, smooth editing experiences
  • 🔍 TypeScript First - Complete type safety with comprehensive IntelliSense support
  • 🧩 Component Library - Pre-built UI components that can be used independently

📦 Installation

npm install react-richtext-tiptap
# or
yarn add react-richtext-tiptap
# or
pnpm add react-richtext-tiptap

Peer Dependencies

Make sure you have these peer dependencies installed:

npm install react react-dom @tiptap/react @tiptap/starter-kit

🚀 Quick Start

Basic Usage

import { Editor } from 'react-richtext-tiptap'

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

  return (
    <Editor
      content={content}
      onChange={setContent}
      placeholder="Start writing..."
    />
  )
}

With Custom Extensions

import { 
  Editor, 
  createDraggableImageExtensions,
  StarterKit,
  Color,
  TextStyle
} from 'react-richtext-tiptap'

const customExtensions = [
  StarterKit,
  TextStyle,
  Color,
  ...createDraggableImageExtensions({
    allowDrag: true,
    maxWidth: 800,
    textWrapMode: 'css'
  })
]

function CustomEditor() {
  return (
    <Editor
      config={{ extensions: customExtensions }}
      toolbar={{ showImage: true, showTextColor: true }}
    />
  )
}

With Image Upload

import { Editor, handleImageDrop } from 'react-richtext-tiptap'

function EditorWithUpload() {
  const uploadImage = async (file: File): Promise<string> => {
    // Upload to your server
    const formData = new FormData()
    formData.append('image', file)
    
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData
    })
    
    const { url } = await response.json()
    return url
  }

  const handleDrop = (editor, files) => {
    handleImageDrop(editor, files, {
      onUpload: uploadImage,
      alignment: 'center',
      maxSize: 5 * 1024 * 1024 // 5MB
    })
  }

  return <Editor onImageDrop={handleDrop} />
}

🎛️ API Reference

Editor Component

The main editor component with full customization options.

interface EditorProps {
  content?: string
  onChange?: (content: string) => void
  onUpdate?: (editor: TiptapEditor) => void
  onSelectionUpdate?: (editor: TiptapEditor) => void
  onFocus?: (editor: TiptapEditor) => void
  onBlur?: (editor: TiptapEditor) => void
  config?: EditorConfig
  toolbar?: ToolbarConfig | boolean
  bubbleMenu?: BubbleMenuConfig | boolean
  floatingMenu?: FloatingMenuConfig | boolean
  theme?: EditorTheme
  className?: string
  children?: ReactNode
}

Hooks

useEditor

The main hook for editor functionality with commands and state.

const { editor, commands, state, content, isEmpty, isFocused } = useEditor(
  initialContent,
  config,
  onChange
)

useEditorCommands

Access editor commands independently.

const commands = useEditorCommands(editor)

// Available commands:
commands.bold()
commands.italic()
commands.addImage('url', 'alt text', 400, 300)
commands.setTextAlign('center')
commands.insertTable(3, 3)
// ... and many more

useEditorState

Reactive editor state information.

const state = useEditorState(editor)

// Available state:
state.isActive.bold
state.canUndo
state.isEmpty
state.selection
// ... and more

Extension Presets

Pre-configured extension sets for different use cases:

import {
  MinimalExtensions,    // Basic text formatting only
  BasicExtensions,      // Essential formatting tools
  RichTextExtensions,   // Advanced formatting without tables
  FullFeaturedExtensions // Everything including drag-and-drop images
} from 'react-richtext-tiptap'

Drag-and-Drop Images

Creating Draggable Images

import { createDraggableImageExtensions } from 'react-richtext-tiptap'

const imageExtensions = createDraggableImageExtensions({
  allowDrag: true,
  textWrapMode: 'css', // or 'custom'
  alignmentOptions: ['left', 'center', 'right', 'inline'],
  maxWidth: 800,
  maxHeight: 600,
  minWidth: 50,
  minHeight: 50,
  maintainAspectRatio: true
})

Image Upload Handler

import { handleImageDrop } from 'react-richtext-tiptap'

await handleImageDrop(editor, files, {
  maxSize: 5 * 1024 * 1024, // 5MB
  allowedTypes: ['image/jpeg', 'image/png', 'image/gif'],
  alignment: 'center',
  onUpload: async (file) => {
    // Your upload logic here
    return await uploadToServer(file)
  }
})

Toolbar Configuration

Customize which buttons appear in the toolbar:

const toolbarConfig: ToolbarConfig = {
  showBold: true,
  showItalic: true,
  showUnderline: true,
  showStrike: true,
  showCode: true,
  showHighlight: true,
  showLink: true,
  showImage: true,
  showHeadings: true,
  showLists: true,
  showQuote: true,
  showCodeBlock: true,
  showTable: true,
  showTextAlign: true,
  showTextColor: true,
  customButtons: [
    {
      name: 'my-button',
      icon: <MyIcon />,
      tooltip: 'Custom Action',
      action: (editor) => {
        editor.chain().focus().insertContent('Custom!').run()
      }
    }
  ]
}

<Editor toolbar={toolbarConfig} />

Theming

Customize the editor appearance:

const customTheme: EditorTheme = {
  colors: {
    primary: '#3B82F6',
    secondary: '#8B5CF6',
    background: '#FFFFFF',
    foreground: '#1F2937',
    muted: '#F9FAFB',
    border: '#E5E7EB'
  },
  borderRadius: '0.5rem',
  spacing: {
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem'
  },
  typography: {
    fontFamily: 'Inter, sans-serif',
    fontSize: '0.875rem',
    lineHeight: '1.5'
  }
}

<Editor theme={customTheme} />

🎨 Styling

The editor is styled with Tailwind CSS but designed to be completely customizable:

CSS Classes

/* Main wrapper */
.dnd-wrapper { /* Drag and drop container */ }

/* Images */
.image-wrapper { /* Image container with drag handles */ }
.draggable-image { /* The actual image element */ }
.image-drag-handle { /* Drag handle overlay */ }
.image-resize-handle { /* Resize corner handles */ }

/* Editor content */
.prose { /* TailwindCSS typography */ }
.editor-link { /* Link styling */ }

Custom CSS

Add your own styles by overriding the CSS classes:

.my-editor .draggable-image {
  border: 2px solid #3B82F6;
  border-radius: 8px;
}

.my-editor .image-drag-handle {
  background: linear-gradient(45deg, #3B82F6, #8B5CF6);
}

🔧 Advanced Usage

Custom Extensions

Create your own TipTap extensions and integrate them:

import { Extension } from '@tiptap/core'

const MyCustomExtension = Extension.create({
  name: 'myCustomExtension',
  
  addCommands() {
    return {
      myCommand: (options) => ({ commands }) => {
        // Your custom logic here
        return commands.insertContent('Custom content!')
      }
    }
  }
})

const extensions = [
  ...FullFeaturedExtensions,
  MyCustomExtension
]

<Editor config={{ extensions }} />

Custom Components

Replace built-in UI components with your own:

import { EditorContent } from '@tiptap/react'
import { useEditor, DragAndDropWrapper } from 'react-richtext-tiptap'

function MyCustomEditor() {
  const { editor } = useEditor()

  return (
    <DragAndDropWrapper editor={editor}>
      <MyCustomToolbar editor={editor} />
      <EditorContent editor={editor} />
      <MyCustomBubbleMenu editor={editor} />
    </DragAndDropWrapper>
  )
}

Handling Events

Listen to editor events for custom behavior:

<Editor
  onUpdate={(editor) => {
    console.log('Content updated:', editor.getHTML())
  }}
  onSelectionUpdate={(editor) => {
    console.log('Selection changed:', editor.state.selection)
  }}
  onFocus={(editor) => {
    console.log('Editor focused')
  }}
  onBlur={(editor) => {
    console.log('Editor blurred')
  }}
/>

📚 Examples

Minimal Blog Editor

import { Editor, BasicExtensions } from 'react-richtext-tiptap'

function BlogEditor({ post, onSave }) {
  return (
    <div className="max-w-4xl mx-auto">
      <input 
        type="text" 
        placeholder="Post title..."
        className="w-full text-2xl font-bold mb-4 p-2"
      />
      <Editor
        content={post.content}
        onChange={(content) => onSave({ ...post, content })}
        config={{ extensions: BasicExtensions }}
        placeholder="Tell your story..."
        className="min-h-[400px]"
      />
    </div>
  )
}

Documentation Editor

import { Editor, FullFeaturedExtensions } from 'react-richtext-tiptap'

function DocsEditor() {
  return (
    <Editor
      config={{ 
        extensions: FullFeaturedExtensions,
        placeholder: 'Start writing your documentation...'
      }}
      toolbar={{
        showHeadings: true,
        showTable: true,
        showCodeBlock: true,
        showImage: true,
        customButtons: [
          {
            name: 'callout',
            icon: <InfoIcon />,
            action: (editor) => {
              editor.chain().focus().insertContent(
                '<blockquote><p>💡 Pro tip: This is a callout!</p></blockquote>'
              ).run()
            }
          }
        ]
      }}
    />
  )
}

🛠️ Development

Building the Library

# Install dependencies
npm install

# Build for production
npm run build

# Run development server
npm run dev

# Run tests
npm run test

# Type checking
npm run type-check

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

📄 License

MIT © React TipTap Team

🙋‍♂️ Support

🔗 Related Projects