| 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: 2026-01-06
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,
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({
heading: {
levels: [1, 2, 3],
},
bulletList: {
keepMarks: true,
},
}),
Image.configure({
inline: true,
allowBase64: false,
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,
],
})
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> {
const reader = new FileReader()
const base64 = await new Promise<string>((resolve) => {
reader.onload = () => resolve(reader.result as string)
reader.readAsDataURL(file)
})
editor.chain().focus().setImage({ src: base64 }).run()
const formData = new FormData()
formData.append('file', file)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const { url } = await response.json()
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)
.tiptap {
@apply prose prose-sm sm:prose-base lg:prose-lg dark:prose-invert max-w-none;
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,
}),
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'
const editor = useEditor({
extensions: [StarterKit, Markdown],
content: '# Hello World\n\nThis is **Markdown**!',
contentType: 'markdown',
immediatelyRender: false,
})
const markdownOutput = editor.getMarkdown()
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:
Questions? Issues?
- Check
references/common-errors.md for troubleshooting
- Verify
immediatelyRender: false is set
- Check official docs: https://tiptap.dev
- Ensure
@tiptap/pm peer dependency is installed