Best for
Founders adding blog authoring, note-taking, or user-generated content fields to a Next.js app
Stack
A ready-made Tiptap Rich Text Editor UI you can fork, run, and customize with the prompt pack below.
What's actually inside
The honest engineer's breakdown — what the Tiptap Rich Text Editortemplate does, how it's wired, and where it's opinionated.
The Tiptap Rich Text Editor template wraps ProseMirror — the battle-tested document editing engine — in a React-friendly shell using the @tiptap/react adapter. The central useEditor hook holds the Editor instance and loads the StarterKit extension bundle, which covers bold, italic, headings, bullet/ordered lists, and blockquotes out of the box. The Toolbar component sits above the canvas and calls editor.chain().focus() commands so every button is immediately functional when you fork.
Two UI elements make this template stand out above a basic textarea. The BubbleMenu floats inline when you select text, giving users context-aware formatting without cluttering the toolbar. The CharacterCount extension renders a live character and word count below the editor — useful for content with limits like bio fields or meta descriptions. The Placeholder extension shows ghost text when the canvas is empty, which is a small UX detail that matters in real products.
Honest caveat: this template has no persistence baked in — content is lost on page refresh unless you wire up a save flow (see the prompts below). The BubbleMenu can also misbehave in V0's esm.sh preview sandbox when multiple @tiptap/* packages resolve from CDN at different versions; this is a preview-only issue that disappears on a real Vercel deployment. If you need real-time collaborative editing or custom block types beyond StarterKit, you will need to extend significantly beyond what's here.
Key UI components
EditorContentRenders the Tiptap ProseMirror canvas into the React DOM
ToolbarBold, Italic, Underline, Heading (H1/H2), Bullet/Ordered list, Blockquote buttons wired to editor.chain().focus() commands
BubbleMenuFloating inline format bar that appears on text selection
CharacterCount displayLive word and character counter rendered below the editor
useEditor hookCentral hook holding the Tiptap Editor instance and extension configuration
Placeholder extensionGhost text shown when the editor canvas is empty
Libraries it leans on
@tiptap/coreProseMirror abstraction layer, manages document state and commands
@tiptap/reactReact adapter: useEditor hook and EditorContent/BubbleMenu components
@tiptap/starter-kitBundled base extensions (bold, italic, headings, lists, blockquote, history)
@tiptap/extension-placeholderGhost-text placeholder when editor is empty
@tiptap/extension-character-countTracks and exposes character and word counts via the CharacterCount display
Fork it and get it running
Forking takes about ten minutes in your browser — no local environment needed until you're ready to hook up a database.
Open the community page and fork
Go to https://v0.dev/chat/community/rcy5rJXu2GO in your browser. Click the blue Fork button in the top-right corner of the page. V0 copies the entire project into your account and immediately opens it in the full editor with the Vercel Sandbox preview on the right.
Tip: You need a free V0 account to fork. If you don't have one, sign up at v0.dev before clicking Fork.
You should see: The editor opens with the Tiptap canvas, Toolbar, BubbleMenu, and CharacterCount visible in the preview pane.
Test the editor in preview
In the Vercel Sandbox preview pane on the right, type some text and try clicking toolbar buttons (Bold, Italic, H1). Select a word to confirm the BubbleMenu floats above your selection. Check that the character counter below the canvas updates live as you type. This confirms the template is working correctly before you make any changes.
Tip: If the BubbleMenu doesn't appear, refresh the preview tab — this is sometimes a CDN resolution delay in the sandbox.
You should see: Toolbar buttons apply formatting, BubbleMenu appears on text selection, and CharacterCount increments correctly.
Rename the project and set up environment variables
Click the project name at the top-left of the V0 editor and type a new name matching your app (e.g., 'My Blog Editor'). If you plan to save content to a database, open the Vars panel (top toolbar) and click + Add Variable to add your Supabase credentials (NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY). These are used in the save prompt below.
You should see: Project is renamed and environment variables are saved and ready for use in Server Actions.
Publish to production
Click Share in the top-right, then open the Publish tab and click 'Publish to Production'. V0 deploys the project to Vercel in 30–60 seconds. You'll get a live Vercel subdomain URL that you can share immediately or use for testing.
You should see: A live URL is shown and the editor is accessible from any browser without the V0 editor wrapper.
Connect your custom domain
Go to vercel.com/dashboard and click your newly deployed project. Navigate to Settings → Domains → Add Domain and enter your custom domain name. Follow the CNAME or A record instructions Vercel provides. DNS changes typically propagate in 5–30 minutes.
Tip: Connect your GitHub repo via the Git panel in V0 first — this lets you push future code changes to the same Vercel deployment.
You should see: Your Tiptap editor is live on your own domain with automatic HTTPS.
The prompt pack
Copy-paste these straight into v0's chat to customize the Tiptap Rich Text Editortemplate. Each one names this template's own components — no generic filler.
Swap placeholder text and set a character soft limit
Updates the Placeholder ghost text and adds a visual warning to the CharacterCount display when the user exceeds 2,000 characters.
Change the Placeholder extension config so the ghost text reads 'Write your post here…' instead of the current default. Also update the CharacterCount display label from 'characters' to 'chars' and add a 2,000-character soft limit that turns the counter red via text-red-500 when exceeded. Keep all other Toolbar and BubbleMenu behavior unchanged.
Apply dark mode to the entire editor
Adds a system-preference-aware dark mode to the EditorContent, Toolbar, and BubbleMenu using Tailwind dark: utilities.
Apply dark mode styling to the EditorContent area, Toolbar, and BubbleMenu. Use Tailwind's dark: prefix so the editor respects the user's system preference via the prefers-color-scheme media query. The editor canvas background should switch to bg-zinc-900 with text-zinc-100, and Toolbar buttons should use dark:bg-zinc-800 dark:text-zinc-200 with active-state highlights that use dark:bg-zinc-700. The BubbleMenu background should use dark:bg-zinc-800 with dark:border-zinc-700.
Add image upload to the Toolbar
Wires a file picker to Vercel Blob storage and inserts uploaded images into the Tiptap EditorContent via the Image extension.
Add an image upload button to the Toolbar component. When clicked, open a file picker that accepts PNG and JPEG files. Upload the selected file to a /api/upload route handler that stores it in Vercel Blob and returns a public URL. Use the Tiptap Image extension to insert the returned URL into the EditorContent at the current cursor position using editor.chain().focus().setImage({ src: url }).run(). Show a loading spinner in the toolbar button while the upload is in progress.Save editor content to Supabase on submit
Adds a save flow that calls editor.getHTML() and persists the result to a Supabase posts table with success/error feedback via Sonner.
Add a Save button below the EditorContent. On click, call editor.getHTML() to get the current HTML content, then send it via a Server Action to a Supabase posts table with columns id (uuid), content (text), and created_at (timestamptz). Use NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY environment variables to initialize the Supabase client. Show a success toast via the shadcn/ui Sonner component after a successful insert, and toast.error() if the insert fails.
Add @mention support with user autocomplete
Adds @mention autocomplete powered by a Supabase profiles lookup, rendered as a Popover with a custom MentionNode inserted into the EditorContent.
Install @tiptap/extension-mention. When the user types '@' inside the EditorContent, fetch a list of usernames from GET /api/users?query= — a Next.js route handler that queries a Supabase profiles table with a ILIKE filter. Render the autocomplete list as a shadcn/ui Popover anchored below the cursor. On selection, insert a styled mention chip using a custom MentionNode with a data-user-id attribute set to the selected profile's id. Persist the serialized document via editor.getJSON() so the mention nodes survive round-trips.
Auth-gate the editor so only signed-in users can write
Adds Clerk server-side auth gating to the editor and disables Toolbar editing for users who are not the post's author.
Wrap the Tiptap EditorContent and Toolbar in a Clerk auth gate. Use Clerk's auth() server function in a Server Component parent to check the session; redirect unauthenticated visitors to /sign-in using Next.js redirect(). On the client, use useAuth() to get userId, and pass it as a data-author-id attribute on the editor wrapper div. Disable the Toolbar buttons by calling editor.setEditable(false) if the viewer's userId does not match the post's author_id fetched from a Supabase posts table.
Gotchas when you extend it
The failures people actually hit when they push this template past its defaults — and the exact fix for each.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefinedWhy: Tiptap's @tiptap/react BubbleMenu or FloatingMenu imports resolve incorrectly in V0's esm.sh preview sandbox when multiple @tiptap/* packages are loaded via CDN at different versions — the named export comes back undefined.
Fix: After forking, check the Preview tab. If the error appears, open the Code tab and confirm all @tiptap/* imports reference the same version pin (e.g., '^2.4.0') in package.json. Redeploy to resolve, since the full Vercel deployment handles npm resolution correctly.
Fix the Tiptap BubbleMenu import error — make sure @tiptap/react, @tiptap/core, and @tiptap/starter-kit are all on the same version in package.json, and that BubbleMenu is imported from '@tiptap/react' not '@tiptap/core'.
ReferenceError: localStorage is not definedWhy: If you add localStorage persistence to autosave draft content, V0 may call localStorage directly in the useEditor initialContent — this runs during SSR on Next.js App Router and throws on the server.
Fix: Move localStorage reads inside a useEffect hook with an empty dependency array, or add 'use client' at the top of the editor file and initialize useEditor's content prop with an empty string on first render.
Fix the localStorage SSR error in the Tiptap editor — move localStorage.getItem('draft') into a useEffect that runs after mount, initializing editor content with an empty string on first render.The component at https://ui.shadcn.com/r/styles/new-york-v4/toast.json was not found. It may not exist at the registry.Why: If you prompt V0 to add toast notifications to the save flow, V0 may reference the deprecated shadcn/ui Toast registry entry which was removed in shadcn v4 in favor of Sonner.
Fix: Use the Sonner component instead — import { Toaster } from 'sonner', add it to your root layout, and call toast.success('Saved!') from the save button handler.
Replace the shadcn toast import with Sonner — import Toaster from 'sonner', add <Toaster /> to the root layout, and call toast.success('Saved!') from the save button handler.Missing @types/canvas-confetti (or similar missing @types/* declaration)Why: When adding visual feedback or special toolbar features, V0 sometimes installs packages without their TypeScript type definitions, causing TypeScript build errors on Vercel.
Fix: Prompt V0 to install any missing @types/* packages for all imports that currently lack TypeScript type declarations.
Check package.json for all third-party imports that are missing @types/* peer packages and add them as devDependencies.
Template vs. custom — the honest call
A forked template gets you far, fast. Here's where it holds up, and where you'll outgrow it.
The template is enough when
- You need a write-once authoring field (blog editor, notes, user bio) with standard formatting options
- You want ProseMirror quality without a two-week integration project
- Your content is stored as HTML or JSON in a Postgres column
- You're building an MVP and will revisit the editor later
Go custom when
- You need collaborative real-time editing — Tiptap Collaboration requires a separate Hocuspocus server not included in this template
- You need custom block types (callouts, embeds, tables with merge cells) beyond what StarterKit provides
- You're integrating with a headless CMS that expects its own content schema (Portable Text, MDX)
If the editor needs to connect to your existing backend, handle file uploads, or add real-time collaboration, RapidDev can extend this V0 prototype to production in a defined scope.
Frequently asked questions
Is the Tiptap Rich Text Editor v0 template free to use?
Yes. V0 community templates are free to fork for any account holder. You need a free v0.dev account to click Fork and clone the project into your workspace. Forking itself does not consume credits.
Can I use this template in a commercial product?
Yes. V0 community templates carry no licensing restrictions on the generated code — you own the output. Tiptap itself is MIT-licensed for the core and starter-kit packages. If you later need Tiptap Pro extensions (collaboration, AI commands), those have a separate commercial license from Tiptap GmbH.
Why does my fork show a blank or broken BubbleMenu in V0 preview?
The BubbleMenu can fail in V0's esm.sh preview sandbox when multiple @tiptap/* packages resolve from CDN at mismatched versions, causing the named export to come back undefined. This is a preview-only issue — deploying to Vercel uses standard npm resolution and the error disappears. In the Code tab, verify all @tiptap/* entries in package.json share the same version pin (e.g., '^2.4.0').
How do I save the editor content to a database?
Call editor.getHTML() to get the current document as an HTML string, or editor.getJSON() to get the ProseMirror JSON document. Send either via a Server Action to your database. The prompt pack above includes a complete Supabase save flow using NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY that you can paste directly into V0's chat.
Does this template support real-time collaborative editing?
No. This template is a single-user editor. Real-time collaboration requires Tiptap's Collaboration extension paired with a Hocuspocus WebSocket server — neither is included here. If collaboration is a requirement from day one, that's a custom build beyond this template.
I get 'ReferenceError: localStorage is not defined' when adding draft autosave — how do I fix it?
This happens when localStorage is accessed during server-side rendering in Next.js App Router. Move all localStorage reads into a useEffect with an empty dependency array so they only run in the browser. Initialize the useEditor content prop with an empty string on first render, then sync from localStorage after mount.
Can RapidDev customize this template for my specific use case?
Yes. If you need the Tiptap editor connected to your existing backend, wired to a headless CMS, or extended with file uploads and real-time collaboration, RapidDev can scope and build that production integration on top of this V0 prototype.
What's the difference between using editor.getHTML() and editor.getJSON() for saving?
editor.getHTML() returns a standard HTML string — easy to render anywhere but lossy for custom node attributes like mention data-user-id. editor.getJSON() returns the full ProseMirror document tree as JSON, which preserves all extension data and is the right choice if you plan to round-trip content back into the editor or need to query mention nodes. For simple blog posts, HTML is fine; for rich content with custom nodes, use JSON.
Outgrowing the template?
RapidDev turns v0 prototypes into production apps — real auth, database, and payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.