April 7, 2026 · Development · 3 min read
CMS first websites
Structured content, typed queries and a studio clients actually use. Why we build every marketing site on Sanity and Next.js.
By 0on0

CMS first websites.
A client once paid for a beautiful marketing site they could not edit. Every headline change meant a developer ticket, a branch, a deploy. The site was correct for exactly eleven days. Then the copy drifted from reality and stayed wrong for months.
We build CMS first so that never happens again. Content lives in Sanity from day one. The site reads it with typed queries. Editors publish without touching code.
Content as data, not pages
Traditional CMS platforms store pages as blobs of HTML. Move the design and the content breaks. Replatform and the migration eats a quarter.
Sanity stores structured JSON in a Content Lake, queried with GROQ. A post is a document with fields: title, slug, excerpt, body blocks, author reference, category reference. The frontend decides how each field renders. Redesign the site and the content flows into the new templates untouched. One agency partner, Bejamas, rebuilt its frontend from Gatsby to Next.js to Remix while Sanity stayed the foundation throughout. That portability is the pitch.
Our own site proves the smaller version. Ten schema types power everything: services, works, case studies, posts, authors, categories, jobs, legal pages, site settings. The homepage, services index, blog, careers and legal routes all read from the same lake. Nothing is hardcoded except honest empty states for unpublished content.
Types end to end
The part developers underestimate is TypeGen. Schemas and queries generate TypeScript types, committed to the repo, verified in CI. Queries stay wrapped in defineQuery so the registry resolves result types automatically.
import { defineQuery } from "next-sanity";
import { sanityFetch } from "@/sanity/lib/client";
export const postsListQuery = defineQuery(`*[
_type == "post" && defined(slug.current)
] | order(publishedAt desc) {
_id, title, "slug": slug.current,
excerpt, publishedAt, readTime
}[0...24]`);
export async function getPosts() {
// Result type inferred from the registry. No hand written duplicate.
return sanityFetch(postsListQuery);
}# After any schema or query edit:
bun run typegen && bun lint && bunx tsc --noEmitThis loop catches an entire class of CMS bugs before deploy: renamed fields, removed references, optional assets without guards. Our image helper narrows optional assets at query boundaries, so a post without a cover renders a muted tile instead of crashing the grid. Boring reliability, generated rather than remembered.
Editing that clients survive
Two Sanity features decide whether clients actually use the studio.
Visual editing maps rendered text back to its source field, so editors click content on the page and land in the right input. No hunting through document lists for a typo.
The Live Content API pushes publishes to the site within seconds, with cache revalidation handled by the toolkit instead of custom webhooks. Our simpler setup revalidates every 60 seconds, so publishes appear without a redeploy and without anyone configuring cache tags. Either path beats the old ticket branch deploy cycle by days.
Embedded Studio sits at /studio on the same domain, outside the marketing layout. No separate login portal, no second deployment. Editors learn one URL.
The honest tradeoffs
CMS first costs modeling time upfront. Defining services with pain points, offerings, stacks, pricing, stats, process steps and FAQs takes longer than hardcoding one page. The payoff arrives at the second page and compounds at the tenth.
It also requires discipline about optionality. Every field that can be empty must render sensibly when empty. Our service detail page hides pain sections, stack marquees and pricing bands when unpublished. Sections appear as content arrives. The site is never broken, only shorter.
WordPress still powers a huge share of the web and passes Core Web Vitals on 38.4 percent of measured origins against 44.1 percent for the rest of the web. The gap is load time, mostly hosting and plugin weight. A well hosted WordPress site performs fine. We choose Sanity and Next.js for a different reason: structured content that survives redesigns, types that catch drift, and an editing experience clients do not fear. Speed comes from the static architecture on top, not from the logo on the box.
Takeaways
- Model content as structured data from day one. Pages are views, not storage.
- Generate types from schemas and queries. Verify with typegen, lint and typecheck on every change.
- Guard every optional field. Empty CMS content should shorten pages, never break them.
- Embed the studio on domain, enable visual editing, revalidate fast. Remove every reason editors file tickets instead of publishing.