The brief
Foniolabs is my personal lab — where I experiment with tech for education and web3 in my own time. Its site was a Next.js static export on shared hosting, and every copy change ran through code. I wanted a CMS migration that didn't degrade SEO or shipping velocity, and three things specifically:
- Editor-friendly content modeling for fast, non-technical editing
- SEO continuity — no ranking drop after the cutover
- An AI-native workflow — content via natural language, not Studio clicking
Constraints
- 14 calendar days, ~45 working hours of mine across that window
- Zero downtime — DNS swap on Day 13
- Free-tier infrastructure (Sanity free, Vercel hobby, GA4 free)
- 100% of public URLs preserved with 301s where slugs changed
What I built
Content model — 9 documents (siteSettings, navigation, page, post, author,
product, teamMember, seo, redirect) + 8 reusable section blocks (hero,
featureGrid, richText, cta, testimonial, logoCloud, embedHtml, contactForm).
Pages compose from sections; I recombine blocks without touching code.
Custom Studio components — a SlugInput that auto-creates a redirect document when a
URL changes; a Google snippet SEOPreview; a live OGImagePreview that renders the
/api/og edge route inline; a presentation tool wired to Next.js draft mode for true live
preview.
Migration script — idempotent, asset-uploading, with a dry-run mode and seeded
singletons that survive re-runs (createIfNotExists). Every legacy URL maps to a new Sanity
document or a 301 redirect.
Claude Code + Sanity MCP — hosted MCP server registered at the repo root via
.mcp.json. From inside Claude Code, "create a news post titled X, set publishedAt to
today, assign author Z" lands real documents in production. Documented in OPERATING.md
with a prompt cheat sheet I can come back to.
Results
- Live on Vercel; Sanity Studio embedded at
/studio - Lighthouse 95+ across Performance, Accessibility, Best Practices, SEO on mobile
- Zero SEO ranking drop after DNS cutover — every legacy URL resolves via 301 or new page
- I publish content by prompting; schema and blocks stay in code
What I learned
Sanity's "compose pages from section blocks" pattern is the difference between a CMS you actually use and a CMS you avoid. Modeling at the block layer — not the page layer — lets you recombine without ever reaching back into a new schema.