Sanity CMS for a one-person portfolio: overkill or the right call?
Most developer portfolios are static files or a hard-coded array of projects. Mine fetches everything from a headless CMS. Here's whether that was worth it.
Most developer portfolios are a JSON file of projects and maybe a markdown blog. They’re simple, fast, and they get the job done. Mine fetches everything — projects, blog posts, author info, site settings — from Sanity, a headless CMS. That was a deliberate choice.
Is this overkill? Yes. Was it worth it? Also yes.
The honest answer sits somewhere between “you definitely don’t need this” and “once you’ve built content-heavy sites this way, you won’t go back.” Here’s the actual tradeoff.
What you actually get
Live content editing without a deploy
Add a project, fix a typo in your bio, update your tagline — none of it requires a git commit or a Vercel rebuild. For a portfolio you touch maybe once a month, this matters more than it sounds. The deploy cycle on a statically generated site is fast, but it’s still friction. With Sanity, the content layer is completely decoupled from the code layer.
Structured data
Projects have typed fields: title, slug, tech stack references, ordered content sections. Blog posts have portable text bodies. Tags are documents, not strings — so filtering and cross-referencing works properly. This is what makes the blog tag filter possible, what drives the auto related posts feature, and what lets the project ordering system work without hardcoding. When your data has shape, you can build features against that shape.
MCP access
Sanity ships an MCP server, which means an AI agent can read and write content directly against the API. That’s how this portfolio is managed — an autonomous agent writes blog posts and creates project entries without touching the codebase. Without structured CMS data and an MCP interface, that workflow would require either file system access or a custom API. Neither is as clean.
GROQ queries
Sanity’s query language lets you fetch exactly what a page needs — nothing more. A single GROQ query can return the blog post, the author, dereferenced tags, and related post candidates, all resolved in one round trip. Compare that to REST, where you’d hit multiple endpoints and stitch results together in application code.
What it costs
Setup complexity
Before you write a single piece of content, you need to: define schemas, deploy them to Sanity, wire sanityFetch into Next.js, configure <SanityLive /> for cache invalidation, and set up the webhook. None of it is difficult, but it’s 2–3 hours of configuration you don’t have with a plain JSON file. The schema-first workflow is also a mindset shift — you’re designing your content model before you build the UI, not after.
Slower development iteration
Every page that fetches from Sanity adds a network call. In production with sanityFetch and CDN-level cache tagging, this is a non-issue. In development, it’s noticeably slower than reading a local file. Not a dealbreaker, but worth knowing before you start.
Another vendor
Sanity is free at this scale (generous free tier), but it’s an external service. Your content lives in their database. If Sanity changes pricing or shuts down, you export your data and migrate. That’s real, manageable friction — but it’s friction that doesn’t exist when your content is files in a git repo.
Overkill for purely static content
If your portfolio hasn’t changed in a year and you have no blog, a JSON file is genuinely the better choice. Sanity earns its complexity when content changes frequently, when cross-referencing matters, and when you want to delegate content management to someone (or something) that isn’t a developer.
The call
Sanity is the right call for a portfolio that has a regularly updated blog, cross-references content through tags and authors, needs live updates without redeploys, or is managed by an AI agent or a non-technical collaborator. That’s four of four for this project.
For a static “here are my 3 projects” site with no blog and no plans to update it more than once a year, it’s overkill. The honest version: use a JSON file or static markdown, deploy to Vercel, and move on with your life.
The line I’d draw: if you’re building features that depend on your content having consistent shape — filtering, cross-referencing, ordering, querying — the CMS pays for itself. If your content is just text and images with no relationships between pieces, the overhead isn’t worth it.
This portfolio lands clearly on the CMS side of that line. I’d make the same choice again.
The source for this portfolio is on GitHub if you want to look at the schema definitions or the Next.js integration. One thing I’d do differently: deploy the schema before writing any content. Deploying it afterward, while documents already exist, is tedious and occasionally confusing.
