Claude Code Plugins

Community-maintained marketplace

Feedback

|

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name tiptap
description Build rich text editors with Tiptap - headless editor framework with React, shadcn/ui, and Tailwind v4 integration. Includes SSR-safe setup, image uploads to R2, prose styling, collaborative editing, and markdown support. Use when creating blog editors, comment systems, documentation platforms, or Notion-like apps, or troubleshooting SSR hydration errors, Tailwind typography issues, or image upload performance.

Tiptap Rich Text Editor

Status: Production Ready Last Updated: 2025-11-29 Dependencies: React 19+, Tailwind v4, shadcn/ui (recommended) Latest Versions: @tiptap/react@3.11.1, @tiptap/starter-kit@3.11.1, @tiptap/pm@3.11.1


Quick Start (5 Minutes)

1. Install Dependencies

npm install @tiptap/react @tiptap/starter-kit @tiptap/pm @tiptap/extension-image @tiptap/extension-color @tiptap/extension-text-style @tiptap/extension-typography

Why this matters:

  • @tiptap/pm is required peer dependency (ProseMirror engine)
  • StarterKit bundles 20+ essential extensions (headings, lists, bold, italic, etc.)
  • Image/color/typography are common additions not in StarterKit

2. Create SSR-Safe Editor

import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

export function Editor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
    immediatelyRender: false, // ⚠️ CRITICAL for SSR/Next.js
    editorProps: {
      attributes: {
        class: 'prose prose-sm focus:outline-none min-h-[200px] p-4',
      },
    },
  })

  return <EditorContent editor={editor} />
}

CRITICAL:

  • Always set immediatelyRender: false for Next.js/SSR apps (prevents hydration mismatch)
  • Without this, you'll see: "SSR has been detected, please set immediatelyRender explicitly to false"
  • This is the #1 error reported by Tiptap users

3. Add Tailwind Typography (Optional but Recommended)

npm install @tailwindcss/typography

Update your tailwind.config.ts:

import typography from '@tailwindcss/typography'

export default {
  plugins: [typography],
}

Why this matters:

  • Provides default prose styling for headings, lists, links, etc.
  • Without it, formatted content looks unstyled
  • Alternative: Use custom Tailwind classes with .tiptap selector

The 3-Step Setup Process

Step 1: Choose Your Integration Method

Option A: shadcn Minimal Tiptap Component (Recommended)

Install the pre-built shadcn component:

npx shadcn@latest add https://raw.githubusercontent.com/Aslam97/shadcn-minimal-tiptap/main/registry/block-registry.json

This installs:

  • Fully-featured editor component with toolbar
  • Image upload support
  • Code block with syntax highlighting
  • Typography extension configured
  • Dark mode support

Option B: Build Custom Editor (Full Control)

Use templates from this skill:

  • templates/base-editor.tsx - Minimal editor setup
  • templates/common-extensions.ts - Extension bundle
  • templates/tiptap-prose.css - Tailwind styling

Key Points:

  • Option A: Faster setup, opinionated UI
  • Option B: Complete customization, headless approach
  • Both work with React + Tailwind v4

Step 2: Configure Extensions

Extensions add functionality to your editor:

import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import Link from '@tiptap/extension-link'
import Typography from '@tiptap/extension-typography'

const editor = useEditor({
  extensions: [
    StarterKit.configure({
      // Customize built-in extensions
      heading: {
        levels: [1, 2, 3],
      },
      bulletList: {
        keepMarks: true,
      },
    }),
    Image.configure({
      inline: true,
      allowBase64: false, // ⚠️ Prevent base64 bloat
      resize: {
        enabled: true,
        directions: ['top-right', 'bottom-right', 'bottom-left', 'top-left'],
        minWidth: 100,
        minHeight: 100,
        alwaysPreserveAspectRatio: true,
      },
    }),
    Link.configure({
      openOnClick: false,
      HTMLAttributes: {
        class: 'text-primary underline',
      },
    }),
    Typography, // Smart quotes, dashes, etc.
  ],
})

CRITICAL:

  • Set allowBase64: false to prevent huge JSON payloads
  • Use upload handler pattern (see templates/image-upload-r2.tsx)
  • Extension order matters - dependencies must load first

Step 3: Handle Image Uploads (If Needed)

Pattern: Base64 preview → background upload → replace with URL

See templates/image-upload-r2.tsx for full implementation:

import { Editor } from '@tiptap/core'

async function uploadImageToR2(file: File, env: Env): Promise<string> {
  // 1. Create base64 preview for immediate display
  const reader = new FileReader()
  const base64 = await new Promise<string>((resolve) => {
    reader.onload = () => resolve(reader.result as string)
    reader.readAsDataURL(file)
  })

  // 2. Insert preview into editor
  editor.chain().focus().setImage({ src: base64 }).run()

  // 3. Upload to R2 in background
  const formData = new FormData()
  formData.append('file', file)

  const response = await fetch('/api/upload', {
    method: 'POST',
    body: formData,
  })

  const { url } = await response.json()

  // 4. Replace base64 with permanent URL
  editor.chain()
    .focus()
    .updateAttributes('image', { src: url })
    .run()

  return url
}

Why this pattern:

  • Immediate user feedback (preview)
  • No database bloat from base64
  • Works with Cloudflare R2
  • Graceful error handling

Critical Rules

Always Do

✅ Set immediatelyRender: false in useEditor() for SSR apps ✅ Install @tailwindcss/typography for prose styling ✅ Use upload handler for images (not base64) ✅ Memoize editor configuration to prevent re-renders ✅ Include @tiptap/pm peer dependency

Never Do

❌ Use immediatelyRender: true (default) with Next.js/SSR ❌ Store images as base64 in database (use URL after upload) ❌ Forget to add prose classes to editor container ❌ Load more than 100 widgets in collaborative mode ❌ Use Create React App (v3 incompatible - use Vite)


Known Issues Prevention

This skill prevents 5 documented issues:

Issue #1: SSR Hydration Mismatch

Error: "SSR has been detected, please set immediatelyRender explicitly to false" Source: GitHub Issue #5856, #5602 Why It Happens: Default immediatelyRender: true breaks Next.js hydration Prevention: Template includes immediatelyRender: false by default

Issue #2: Editor Re-renders on Every Keystroke

Error: Laggy typing, poor performance in large documents Source: Tiptap Performance Docs Why It Happens: useEditor() hook re-renders component on every change Prevention: Use useEditorState() hook or memoization patterns (see templates)

Issue #3: Tailwind Typography Not Working

Error: Headings/lists render unstyled, no formatting visible Source: shadcn Tiptap Discussion Why It Happens: Missing @tailwindcss/typography plugin Prevention: Skill includes typography plugin installation in checklist

Issue #4: Image Upload Base64 Bloat

Error: JSON payloads become megabytes, slow saves, database bloat Source: Tiptap Image Docs Why It Happens: Default allows base64, no upload handler configured Prevention: R2 upload template with URL replacement pattern

Issue #5: Build Errors in Create React App

Error: "jsx-runtime" module resolution errors after upgrading to v3 Source: GitHub Issue #6812 Why It Happens: CRA incompatibility with v3 module structure Prevention: Skill documents Vite as preferred bundler + provides working config


Configuration Files Reference

Tailwind Prose Styling (tiptap-prose.css)

/* Apply to editor container */
.tiptap {
  /* Tailwind Typography */
  @apply prose prose-sm sm:prose-base lg:prose-lg dark:prose-invert max-w-none;

  /* Custom overrides */
  h1 {
    @apply text-3xl font-bold mt-8 mb-4;
  }

  h2 {
    @apply text-2xl font-semibold mt-6 mb-3;
  }

  p {
    @apply my-4 text-base leading-7;
  }

  ul, ol {
    @apply my-4 ml-6;
  }

  code {
    @apply bg-muted px-1.5 py-0.5 rounded text-sm font-mono;
  }

  pre {
    @apply bg-muted p-4 rounded-lg overflow-x-auto;
  }

  blockquote {
    @apply border-l-4 border-primary pl-4 italic my-4;
  }
}

Why these settings:

  • prose classes provide consistent formatting
  • dark:prose-invert handles dark mode automatically
  • Custom overrides use semantic Tailwind v4 colors

Common Patterns

Pattern 1: Collaborative Editing with Y.js

import { useEditor } from '@tiptap/react'
import Collaboration from '@tiptap/extension-collaboration'
import * as Y from 'yjs'

const ydoc = new Y.Doc()

const editor = useEditor({
  extensions: [
    StarterKit.configure({
      history: false, // Disable history for collaboration
    }),
    Collaboration.configure({
      document: ydoc,
    }),
  ],
})

When to use: Real-time multi-user editing (Notion-like) See: templates/collaborative-setup.tsx for full example

Pattern 2: Markdown Support

import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { Markdown } from '@tiptap/markdown'

// Load editor with markdown content
const editor = useEditor({
  extensions: [StarterKit, Markdown],
  content: '# Hello World\n\nThis is **Markdown**!',
  contentType: 'markdown',  // ⚠️ CRITICAL: Must specify or content parsed as HTML
  immediatelyRender: false,
})

// Get markdown from editor
const markdownOutput = editor.getMarkdown()

// Insert markdown content
editor.commands.setContent('## New heading', { contentType: 'markdown' })
editor.commands.insertContent('**Bold** text', { contentType: 'markdown' })

When to use: Storing content as markdown, displaying/editing rich text Install: npm install @tiptap/markdown@3.11.1 Status: Beta (released Oct 2025, API stable but may change) CRITICAL: Always specify contentType: 'markdown' when setting markdown content

Pattern 3: Form Integration with react-hook-form

import { useForm, Controller } from 'react-hook-form'

function BlogForm() {
  const { control, handleSubmit } = useForm()

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="content"
        control={control}
        render={({ field }) => (
          <Editor
            content={field.value}
            onUpdate={({ editor }) => {
              field.onChange(editor.getHTML())
            }}
          />
        )}
      />
    </form>
  )
}

When to use: Blog posts, comments, any form-based content


Using Bundled Resources

Scripts (scripts/)

No executable scripts for this skill.

Templates (templates/)

Required for all projects:

  • templates/base-editor.tsx - Minimal React editor component
  • templates/package.json - Required dependencies

Optional based on needs:

  • templates/minimal-tiptap-setup.sh - shadcn component installation
  • templates/image-upload-r2.tsx - R2 upload handler
  • templates/tiptap-prose.css - Tailwind styling
  • templates/collaborative-setup.tsx - Y.js collaboration
  • templates/common-extensions.ts - Extension bundle

When to load these: Claude should reference templates when user asks to:

  • Set up tiptap editor
  • Add image uploads
  • Configure collaborative editing
  • Style with Tailwind prose

References (references/)

  • references/tiptap-docs.md - Key documentation links
  • references/common-errors.md - Error troubleshooting guide
  • references/extension-catalog.md - Popular extensions list

When Claude should load these: Troubleshooting errors, exploring extensions, understanding API


Advanced Topics

Custom Extensions

Create your own Tiptap extensions:

import { Node } from '@tiptap/core'

const CustomNode = Node.create({
  name: 'customNode',

  group: 'block',

  content: 'inline*',

  parseHTML() {
    return [{ tag: 'div[data-custom]' }]
  },

  renderHTML({ HTMLAttributes }) {
    return ['div', { 'data-custom': '', ...HTMLAttributes }, 0]
  },

  addCommands() {
    return {
      insertCustomNode: () => ({ commands }) => {
        return commands.insertContent({ type: this.name })
      },
    }
  },
})

Use cases: Custom widgets, embeds, interactive elements

Slash Commands

Add Notion-like / commands:

import { Extension } from '@tiptap/core'
import Suggestion from '@tiptap/suggestion'

const SlashCommands = Extension.create({
  name: 'slashCommands',

  addOptions() {
    return {
      suggestion: {
        char: '/',
        items: ({ query }) => {
          return [
            { title: 'Heading 1', command: ({ editor, range }) => {
              editor.chain().focus().deleteRange(range).setHeading({ level: 1 }).run()
            }},
            { title: 'Bullet List', command: ({ editor, range }) => {
              editor.chain().focus().deleteRange(range).toggleBulletList().run()
            }},
          ]
        },
      },
    }
  },

  addProseMirrorPlugins() {
    return [Suggestion({ editor: this.editor, ...this.options.suggestion })]
  },
})

Use cases: Productivity shortcuts, quick formatting


Dependencies

Required:

  • @tiptap/react@^3.11.1 - React integration
  • @tiptap/starter-kit@^3.11.1 - Essential extensions bundle
  • @tiptap/pm@^3.11.1 - ProseMirror peer dependency
  • react@^19.0.0 - React framework

Optional:

  • @tiptap/extension-image@^3.11.1 - Image support
  • @tiptap/extension-link@^3.11.1 - Link support (NEW in v3, included in StarterKit)
  • @tiptap/extension-color@^3.11.1 - Text color
  • @tiptap/extension-typography@^3.11.1 - Smart typography
  • @tiptap/extension-collaboration@^3.11.1 - Real-time collaboration
  • @tailwindcss/typography@^0.5.15 - Prose styling
  • yjs@^13.6.0 - Collaborative editing backend
  • react-medium-image-zoom@^5.2.0 - Image zoom functionality

Official Documentation


Package Versions (Verified 2025-11-29)

{
  "dependencies": {
    "@tiptap/react": "^3.11.1",
    "@tiptap/starter-kit": "^3.11.1",
    "@tiptap/pm": "^3.11.1",
    "@tiptap/extension-image": "^3.11.1",
    "@tiptap/extension-color": "^3.11.1",
    "@tiptap/extension-text-style": "^3.11.1",
    "@tiptap/extension-typography": "^3.11.1",
    "@tiptap/extension-link": "^3.11.1"
  },
  "devDependencies": {
    "@tailwindcss/typography": "^0.5.15",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}

Production Example

This skill is based on real-world implementations:

  • GitLab: Uses Tiptap for issue/MR descriptions
  • Statamic CMS: Tiptap as default rich text editor
  • shadcn minimal-tiptap: 3.14M downloads/week

Token Savings: ~71% (14k → 4k tokens) Errors Prevented: 5/5 critical setup errors Validation: ✅ SSR compatibility, ✅ Image uploads, ✅ Tailwind v4, ✅ Performance


Troubleshooting

Problem: "SSR has been detected, please set immediatelyRender explicitly to false"

Solution: Add immediatelyRender: false to your useEditor() config

Problem: Headings/lists look unstyled

Solution: Install @tailwindcss/typography and add prose classes to editor container

Problem: Editor lags when typing

Solution: Use useEditorState() hook instead of useEditor() for read-only rendering, or memoize editor configuration

Problem: Images make JSON huge

Solution: Set allowBase64: false in Image extension config and use upload handler (see templates/image-upload-r2.tsx)

Problem: Build fails in Create React App

Solution: Switch to Vite - CRA incompatible with Tiptap v3. See cloudflare-worker-base skill for Vite setup.


Complete Setup Checklist

Use this checklist to verify your setup:

  • Installed @tiptap/react, @tiptap/starter-kit, @tiptap/pm
  • Set immediatelyRender: false in useEditor() config
  • Installed @tailwindcss/typography plugin
  • Added prose classes to editor container
  • Configured image upload handler (if using images)
  • Set allowBase64: false in Image extension
  • Editor renders without hydration errors
  • Formatted text displays correctly (headings, lists, etc.)
  • Dev server runs without TypeScript errors
  • Production build succeeds

Questions? Issues?

  1. Check references/common-errors.md for troubleshooting
  2. Verify immediatelyRender: false is set
  3. Check official docs: https://tiptap.dev
  4. Ensure @tiptap/pm peer dependency is installed