reactjs-tiptap-editor
SKILL.md
ReactJS Tiptap Editor Skill
When to use
- Building admin content editors (posts, projects, pages).
- Implementing WYSIWYG editing with Markdown output.
- Adding rich text capabilities to forms.
Guardrails
- Admin-only: This editor is for authenticated admin users only.
- Markdown output: Always export as Markdown to store in DB (
content_mdcolumn). - Sanitize on render: When displaying content, parse Markdown → safe HTML (see content-format doc).
- Size: This is a heavy component (~200KB); lazy-load on admin routes.
Workflow checklist
- Install if not present:
bun add reactjs-tiptap-editor - Import editor component in admin route/component.
- Configure extensions (bold, italic, headings, lists, code blocks, links, images).
- Set
output="markdown"to get Markdown strings. - Store Markdown in DB; render with unified/remark/rehype on public pages.
Setup pattern
// domains/blog/ui/PostEditor.tsx (admin only)
import { RichTextEditor } from 'reactjs-tiptap-editor'
import { useState } from 'react'
export function PostEditor({ initialContent }: { initialContent?: string }) {
const [content, setContent] = useState(initialContent ?? '')
return (
<RichTextEditor
content={content}
onChange={(newContent) => setContent(newContent)}
output="markdown" // ← Critical: outputs Markdown
dark={true} // Match theme
extensions={[
'bold', 'italic', 'strike', 'code',
'heading', 'bulletList', 'orderedList',
'codeBlock', 'blockquote',
'link', 'image'
]}
/>
)
}
Integration with domain patterns
// domains/blog/server/create-post.server.ts
import { createServerFn } from '@tanstack/start'
import { CreatePostInput } from '../schema'
export const createPost = createServerFn({ method: 'POST' })
.validator(CreatePostInput)
.handler(async ({ data }) => {
// data.content_md comes from Tiptap editor as Markdown
const supabase = getSupabaseClient()
const { data: post, error } = await supabase
.from('posts')
.insert({ ...data, content_md: data.content_md }) // ← Store Markdown
.select()
.single()
if (error) throw error
return post
})
Best practices
- ✅ Lazy-load: use
.lazy.tsxfor admin routes to avoid bloating public bundle - ✅ Markdown output: always use
output="markdown"to avoid storing HTML - ✅ Dark mode: match
dark={true}to repo's futuristic theme - ✅ Limited extensions: only enable what users need
- ❌ Don't store raw HTML from Tiptap (security risk)
- ❌ Don't load Tiptap on public routes (bundle size)
Rendering on public pages
// src/routes/blog/$slug.tsx (public)
import { parseMarkdown } from '~/lib/markdown/processor'
export const Route = createFileRoute('/blog/$slug')({
loader: async ({ params }) => {
const post = await getPost(params.slug)
const contentHtml = await parseMarkdown(post.content_md) // ← Safe HTML
return { post, contentHtml }
}
})
function BlogPost() {
const { contentHtml } = Route.useLoaderData()
return (
<article>
<div dangerouslySetInnerHTML={{ __html: contentHtml }} />
</article>
)
}
References
- Library patterns: docs/dev-1/docs/16-library-patterns.md#reactjs-tiptap-editor-rich-text
- Content format: docs/dev-1/docs/10-content-format.md (Markdown pipeline)
- Admin spec: docs/dev-1/docs/08-admin-dashboard-spec.md
Tooling
- Use Tavily or Context7 for reactjs-tiptap-editor API questions.
- Demo site: https://reactjs-tiptap-editor.vercel.app/
- LLM context: https://reactjs-tiptap-editor.vercel.app/llms-full.txt
Weekly Installs
3
Repository
huynhsang2005/b…tanstackFirst Seen
Jan 24, 2026
Security Audits
Installed on
trae2
claude-code1