The Story of Building a CMS with Convex and Claude
Why Not Just Use an Existing CMS?
Most CMSes today still give you the same content editing experience from 25 years ago. Bold, italic, heading, list etc. That's it. And if you want something like a custom slider or grid layout inside one specific post but nowhere else, options are usually: create a custom field, build a template for it, and repeat that for every new use case.
No thanks.
What I actually wanted was a flexible editing experience where I can design the content itself, not just fill in a layout that is strictly defined. Features I needed:
Live, reactive backend
Visual block-level control over the content, not just the layout around it
Page builder-like flexibility inside posts and pages
Something close to what WordPress has with its block editor, but reactive and built for how I work. As far as I know, nothing else at this scale offers all three out of the box.
The site was already on Next.js. The database layer was already Convex. So the real question was: can I build a CMS that lives inside my own codebase, talks directly to my existing data, and takes maybe a weekend to ship?
Yes. The PoC(Proof of Concept) took a weekend. Here is how it went.
Why Convex?
Convex is a real-time backend platform that replaces your database, server functions, auth and API layer in one. You write your queries and mutations in TypeScript, and Convex handles syncing, caching, and live updates automatically. No REST endpoints to design. No ORM to configure. No separate API server to maintain.
Bonus: It's AI ready at start.
For a CMS, this is a surprisingly good fit. Posts are just documents. The admin UI needs to read and write them. The public site needs to query them. With Convex, all of that is the same data model, the same schema, the same codebase. There is no sync step. No webhook to trigger a rebuild. When you hit save, the post is live in milliseconds. Like literally.
I already had Convex set up for other parts of the site. Adding a posts table took about ten minutes. The hard part was everything on top of it.
The Schema: Keeping It Simple on Purpose
I told Claude Code what I needed: posts with a title, slug, rich text JSON content, draft/published status, SEO metadata, tags, cover image, and timestamps. It scaffolded the Convex schema in one pass. Clean TypeScript types, proper optional fields, everything validated before it touches the database.
One early decision: no versioning, no revision history, no complex relationships. This is a solo portfolio blog, not a publishing platform. Every extra feature is a feature to maintain. The schema has exactly what I need and nothing more.
The Editor: Tiptap Was the Right Call
A CMS is only as good as its authoring experience. I didn't want to write posts in a textarea and mentally picture the markdown. I wanted something that felt like structuring content, not coding.
I went with Tiptap, a headless rich text editor built on ProseMirror. It stores content as JSON, which plays perfectly with Convex, and gives you full control over rendered output without opinions on your UI.
I had Claude Code wire up the full setup:
Headings, bold, italic, inline code, lists, blockquotes, links, images, and a horizontal divider. All extendable and style-able with Tailwind.
Plus columns, video embeds, and styled blocks I can target with any Tailwind class.
What would have taken a full day of reading ProseMirror docs took about an hour of prompting and reviewing.
The one thing that needed extra iteration was the image upload flow. I wanted images going to Convex file storage, not an external CDN. Claude got the upload logic right on the second pass, but the progress indicator took two more rounds before it felt right. Small things always need that last human review pass.
The Admin UI: Functional Over Fancy
The admin lives at /admin and is protected by an auth check. No user management, no roles, no OAuth. It is just me. One password. One route guard.
The posts list page shows title, slug, status, and last updated date. Each post has three actions: preview, edit, and delete. That is the entire interface. Claude Code built the list component, the routing structure, and the delete confirmation in one prompt. The sidebar navigation with icons came together in another.
The editing experience is where I spent the most design time. The post settings panel on the right handles slug, excerpt, tags, cover image, and SEO fields. All of it collapses into an inspector-style sidebar that stays out of the way when you are writing and opens when you need it. That layout decision was mine. Claude built the component.
The Public Renderer: From JSON to Pixels
Tiptap stores content as a JSON document tree. On the public-facing post page, you need to turn that tree into HTML. Tiptap ships a server-side utility for this called generateHTML, which takes the JSON and a list of extensions and outputs clean markup.
Claude built the renderer component and the Tailwind prose styles that govern how headings, paragraphs, code blocks, and images look on the page. This is the part I iterated on the most visually, tweaking spacing, font sizes, and line heights until it matched the overall design system. The code was fast to generate. The taste decisions were slower, but that is how it should be.
One important detail: the post page is a Next.js Server Component that fetches directly from Convex at request time. No static generation with revalidation, no stale data. When a post is published, it appears immediately. When I fix a typo, it is live in seconds. For a low-traffic personal site, this trade-off is fine. Speed of iteration matters more than edge caching.
How Claude Code Actually Helped
This is the part worth being specific about, because "I used AI to build it" can mean a lot of different things.
Claude Code is a terminal-based coding agent. It reads your codebase, understands the context, and writes code that fits the patterns already in place. I did not paste snippets into a chat window. I described what I wanted in natural language, Claude read the existing files for context, and then it wrote or modified the actual files in my project. It also ran the dev server, caught TypeScript errors, and iterated on its own output until things compiled.
The things Claude did well: wiring up Convex mutations and queries, scaffolding React component structure, handling edge cases I did not think to specify (like slug uniqueness validation), and writing the boilerplate that would have been tedious and error-prone to type manually.
The things that still needed me:
Every visual decision, the information architecture of the admin UI, what features to build versus leave out, and reviewing the output with enough context to catch when something was technically correct but wrong for the project. Claude does not know what you want to feel like. That part is still fully on you.
What I Would Do Differently
Draft autosave. Right now, saving is manual and there is no recovery if you close the tab mid-session. I have not lost anything yet, but I will. That is the one feature gap I am actively aware of and deliberately ignoring until it hurts enough to fix.
I also underestimated how much time I would spend on the Tailwind prose styles for the rendered post. The editor and the public page look different by default, and getting them to feel consistent took more passes than I expected. Next time I would define the typography system first and build both views against it from the start.
Is This Worth Building vs. Using Something Off the Shelf?
For most people, no. Sanity, Payload CMS, or a headless WordPress setup will get you there faster with less risk. Building your own CMS is a classic developer trap, and I walked into it knowingly.
But context matters. The whole site was already an experiment in what you can build when implementation cost drops close to zero. Adding a custom CMS felt consistent with that. The result is something I understand completely, can extend without reading anyone else's docs, and has exactly zero features I don't use.
The thing I keep coming back to is that a weekend to build a working CMS is not a weekend because I am fast. It is a weekend because the activation energy for each step dropped dramatically:
Scaffold a schema, prompt
Wire up the editor, prompt
Build the admin list view, prompt
The decisions still take time. The implementation mostly does not. And you are reading this post through it right now. Cheers.