Development
tiptap - Claude MCP Skill
Build rich text editors with Tiptap - headless editor framework with React and Tailwind v4. Covers SSR-safe setup, image uploads, prose styling, and collaborative editing. Use when creating blog editors, comment systems, or Notion-like apps, or troubleshooting SSR hydration errors, typography issues, or image upload problems.
SEO Guide: Enhance your AI agent with the tiptap tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to build rich text editors with tiptap - headless editor framework with react and tailwind v4. covers s... Download and configure this skill to unlock new capabilities for your AI workflow.
Documentation
SKILL.md# Tiptap Rich Text Editor
**Status**: Production Ready
**Last Updated**: 2026-01-21
**Dependencies**: React 19+, Tailwind v4, shadcn/ui (recommended)
**Latest Versions**: @tiptap/react@3.16.0, @tiptap/starter-kit@3.16.0, @tiptap/pm@3.16.0 (verified 2026-01-21)
---
## Quick Start (5 Minutes)
### 1. Install Dependencies
```bash
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
**Important**: If using Tiptap v3.14.0+, drag handle functionality requires minimum v3.14.0 (regression fixed in that release). For Pro extensions with drag handles, React 18 is recommended due to tippyjs-react dependency.
### 2. Create SSR-Safe Editor
```typescript
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)
```bash
npm install @tailwindcss/typography
```
Update your `tailwind.config.ts`:
```typescript
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:
```bash
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:
```typescript
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:
```typescript
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 **7** documented issues:
### Issue #1: SSR Hydration Mismatch
**Error**: "SSR has been detected, please set `immediatelyRender` explicitly to `false`"
**Source**: [GitHub Issue #5856](https://github.com/ueberdosis/tiptap/issues/5856), [#5602](https://github.com/ueberdosis/tiptap/issues/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](https://tiptap.dev/docs/editor/api/editor#immediatelyrender)
**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](https://github.com/shadcn-ui/ui/discussions/1729)
**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](https://tiptap.dev/docs/editor/extensions/nodes/image#usage)
**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](https://github.com/ueberdosis/tiptap/issues/6812)
**Why It Happens**: CRA incompatibility with v3 module structure
**Prevention**: Skill documents Vite as preferred bundler + provides working config
### Issue #6: ProseMirror Multiple Versions Conflict
**Error**: `Error: Looks like multiple versions of prosemirror-model were loaded`
**Source**: [GitHub Issue #577](https://github.com/ueberdosis/tiptap/issues/577) (131 comments), [Issue #6171](https://github.com/ueberdosis/tiptap/issues/6171)
**Why It Happens**: Installing additional Tiptap extensions can pull different versions of prosemirror-model or prosemirror-view, creating duplicate dependencies in node_modules. The unique-id extension is particularly problematic in testing environments.
**Prevention**: Use package resolutions to force a single ProseMirror version
```json
// package.json
{
"resolutions": {
"prosemirror-model": "~1.21.0",
"prosemirror-view": "~1.33.0",
"prosemirror-state": "~1.4.3"
}
}
```
Or reinstall dependencies:
```bash
rm -rf node_modules package-lock.json
npm install
```
**Note**: The @tiptap/pm package is designed to prevent this issue, but extensions may still introduce conflicts.
### Issue #7: EditorProvider vs useEditor Confusion (Community-sourced)
**Error**: `SSR has been detected, please set 'immediatelyRender' explicitly to 'false'` (when both used together)
**Source**: [GitHub Issue #5856 Comment](https://github.com/ueberdosis/tiptap/issues/5856#issuecomment-2493124171)
**Why It Happens**: Users commonly use EditorProvider and useEditor together, but EditorProvider is a wrapper around useEditor for React Context setup - they should not be used simultaneously.
**Prevention**: Choose one pattern only
**Incorrect Pattern**:
```typescript
// Don't use both together
<EditorProvider>
<MyComponent />
</EditorProvider>
function MyComponent() {
const editor = useEditor({ ... }) // ā Wrong - EditorProvider already created editor
}
```
**Correct Patterns**:
```typescript
// Option 1: Use EditorProvider only
<EditorProvider immediatelyRender={false} extensions={[StarterKit]}>
<EditorContent />
</EditorProvider>
// Option 2: Use useEditor only
function Editor() {
const editor = useEditor({
extensions: [StarterKit],
immediatelyRender: false,
})
return <EditorContent editor={editor} />
}
```
---
## Configuration Files Reference
### Tailwind Prose Styling (tiptap-prose.css)
```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
```typescript
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
```typescript
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.16.0`
**Status**: Beta (released Oct 2025, API stable but may change)
**CRITICAL**: Always specify `contentType: 'markdown'` when setting markdown content
**Recent Fixes** (v3.15.0-v3.16.0):
- Fixed incorrect Markdown output when underline is mixed with bold/italic and ranges don't fully overlap
- Improved serialization for overlapping formatting marks
- Source: [v3.16.0 Release](https://github.com/ueberdosis/tiptap/releases/tag/v3.16.0)
### Pattern 3: Form Integration with react-hook-form
```typescript
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:
```typescript
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:
```typescript
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.16.0` - React integration (React 19 supported)
- `@tiptap/starter-kit@^3.16.0` - Essential extensions bundle
- `@tiptap/pm@^3.16.0` - ProseMirror peer dependency
- `react@^19.0.0` - React framework
**React Version Compatibility**:
- **Core Tiptap**: Supports React 19 as of v2.10.0
- **UI Components**: Work best with React 18 (Next.js 15 recommended per [official docs](https://tiptap.dev/docs/editor/getting-started/install/nextjs))
- **Pro Extensions**: May require React 18 - drag-handle extension depends on archived tippyjs-react without React 19 support ([Issue #5876](https://github.com/ueberdosis/tiptap/issues/5876))
**Optional**:
- `@tiptap/extension-audio@^3.16.0` - Audio support (NEW in v3.16.0)
- `@tiptap/extension-image@^3.16.0` - Image support
- `@tiptap/extension-link@^3.16.0` - Link support (NEW in v3, included in StarterKit)
- `@tiptap/extension-color@^3.16.0` - Text color
- `@tiptap/extension-typography@^3.16.0` - Smart typography
- `@tiptap/extension-collaboration@^3.16.0` - Real-time collaboration
- `@tiptap/extension-markdown@^3.16.0` - Markdown support (Beta)
- `@tailwindcss/typography@^0.5.19` - Prose styling
- `yjs@^13.6.0` - Collaborative editing backend
- `react-medium-image-zoom@^5.2.0` - Image zoom functionality
---
## Official Documentation
- **Tiptap**: https://tiptap.dev
- **Installation Guide**: https://tiptap.dev/docs/editor/installation/react
- **Extensions**: https://tiptap.dev/docs/editor/extensions
- **API Reference**: https://tiptap.dev/docs/editor/api/editor
- **shadcn minimal-tiptap**: https://github.com/Aslam97/shadcn-minimal-tiptap
- **Context7 Library ID**: tiptap/tiptap
---
## Package Versions (Verified 2026-01-21)
```json
{
"dependencies": {
"@tiptap/react": "^3.16.0",
"@tiptap/starter-kit": "^3.16.0",
"@tiptap/pm": "^3.16.0",
"@tiptap/extension-audio": "^3.16.0",
"@tiptap/extension-image": "^3.16.0",
"@tiptap/extension-color": "^3.16.0",
"@tiptap/extension-text-style": "^3.16.0",
"@tiptap/extension-typography": "^3.16.0",
"@tiptap/extension-link": "^3.16.0",
"@tiptap/extension-markdown": "^3.16.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.19",
"react": "^19.2.3",
"react-dom": "^19.2.3"
}
}
```
---
## 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**: 7/7 documented errors (5 critical setup + 2 community patterns)
**Validation**: ā
SSR compatibility, ā
Image uploads, ā
Tailwind v4, ā
Performance, ā
React 19 compatibility
---
## 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 installedSignals
Information
- Repository
- jezweb/claude-skills
- Author
- jezweb
- Last Sync
- 2/18/2026
- Repo Updated
- 2/17/2026
- Created
- 1/16/2026
Reviews (0)
No reviews yet. Be the first to review this skill!
Related Skills
pr-status
PR Status
mem0
Integrate Mem0 Platform into AI applications for persistent memory, personalization, and semantic search. Use this skill when the user mentions "mem0", "memory layer", "remember user preferences", "persistent context", "personalization", or needs to add long-term memory to chatbots, agents, or AI apps. Covers Python and TypeScript SDKs, framework integrations (LangChain, CrewAI, Vercel AI SDK, OpenAI Agents SDK, Pipecat), and the full Platform API. Use even when the user doesn't explicitly say "mem0" but describes needing conversation memory, user context retention, or knowledge retrieval across sessions.
upgrade-nodejs
Upgrading Bun's Self-Reported Node.js Version
cursorrules
CrewAI Development Rules
Related Guides
Bear Notes Claude Skill: Your AI-Powered Note-Taking Assistant
Learn how to use the bear-notes Claude skill. Complete guide with installation instructions and examples.
Mastering tmux with Claude: A Complete Guide to the tmux Claude Skill
Learn how to use the tmux Claude skill. Complete guide with installation instructions and examples.
OpenAI Whisper API Claude Skill: Complete Guide to AI-Powered Audio Transcription
Learn how to use the openai-whisper-api Claude skill. Complete guide with installation instructions and examples.