What a web CMS actually owes you: my baseline standard
By Nayan Kyada · · 6 min read
Part of The Sanity + Next.js Guide
A web CMS is not a database wrapper with a nice UI. It is the operational centre of a content team and the API contract a developer builds against for years. Most platforms fail on at least one of those two jobs. Here is the standard I have arrived at after shipping enough of these projects to know what breaks down in production.
The draft and preview contract is non-negotiable
Every web CMS I recommend must support a first-class draft state that is completely isolated from published content. Not a "preview" flag on the same document. An actual separate document state that can be routed through a secure preview URL without leaking into public API responses.
The failure mode I see most often: a CMS that returns drafts in the default query unless you explicitly filter them out. That is backwards. Drafts should require opt-in access — a token, a cookie, an edge flag — not opt-out filtering that a tired developer forgets on a deadline.
On the Next.js side this pairs with Route Handlers and the draft mode cookie. On the CMS side it requires perspective-aware content delivery: the same GROQ query (or REST call) should return published data by default and draft data only when a valid secret is present. If a platform cannot do that cleanly, editors will break live pages while saving, and that ends trust in the system fast.
Structured content, not rich text blobs
A web CMS that stores page content as a single HTML or Markdown string is a publishing tool, not a content system. Structured content means each piece of content is a typed document with discrete fields: a string title, a reference to an author document, an array of typed block content, an image with crop and hotspot metadata stored separately from the URL.
Why does this matter? Because structured content is queryable, transformable, and renderable in multiple contexts without scraping HTML. You can build a table-of-contents from block headings without parsing HTML. You can serve a plain-text summary to a search index without stripping tags. You can push content to a mobile app, an email template, and a web page from the same source without maintaining three copies.
Rich text blocks are fine inside a structured document — Portable Text handles this well — but the document itself must be structured. If the CMS treats every page as a freeform WYSIWYG canvas with no schema enforcement, you end up querying a blob and the developer API surface is effectively zero.
Roles and permissions that map to real teams
A basic author/admin split is not enough. Real teams have at least three operational roles: someone who writes drafts and can never publish without review, someone who reviews and publishes but cannot touch schema or studio configuration, and a developer-admin who controls everything.
Beyond role-based access, a mature web CMS needs document-level permissions. A regional marketing team should not be able to edit the global homepage even if they are editors. A contractor should be able to write in one document type and read-only everything else.
The failure mode here is usually a CMS that has roles but no path rules or document-type filtering. Everyone with the "editor" role can edit every document, and when that causes an incident — and it will — the only fix is a custom middleware layer that the developer has to maintain forever.
Versioning that editors can actually use
Version history should be a first-class CMS feature, not an afterthought or an enterprise add-on. Editors need to see who changed a document, when, and what changed. They need to restore a previous version without filing a ticket.
The bar I hold CMS platforms to: every published change creates a named, browsable history entry. Restoring does not require developer access. Diff views show field-level changes, not a JSON dump.
This is one area where many headless platforms still lag behind WordPress. The irony is that WordPress's revision history is one of the reasons content teams resist migrating even when the technical case for headless is clear. If a modern web CMS cannot match a 2004-era feature, that is a real objection, not a comfort-zone complaint.
The developer API surface must be typed and predictable
A CMS that requires runtime JSON parsing with no type information is a liability in a TypeScript codebase. The developer API surface I consider baseline in 2026:
- Schema definitions in code (not only a GUI), ideally colocated with the codebase so schema and query types travel together
- Generated TypeScript types from the schema so query results are typed at the call site
- A query language that is expressive enough to shape the response — projections, joins, conditionals — so the client receives only what it needs rather than fetching whole documents and discarding 80% of the fields
// groq/post.ts — generated type from Sanity TypeGen
import { defineQuery } from 'next-sanity'
import type { POST_QUERY_RESULT } from '@/sanity.types'
export const POST_QUERY = defineQuery(`
*[_type == "post" && slug.current == $slug][0] {
title,
publishedAt,
"author": author->{ name, image },
body
}
`)
// In your RSC:
// const post: POST_QUERY_RESULT = await client.fetch(POST_QUERY, { slug })
// post.author.name is typed — no casting, no anyIf the CMS requires REST with no type generation path, that is workable. If it requires GraphQL with a code-gen step, that is workable. If it requires hand-written interfaces that drift from the actual schema because nothing enforces the contract, that is a maintenance debt that compounds.
What a weak web CMS looks like in practice
The tell is usually the handoff. A CMS that fails the standards above produces the same handoff problems every time: editors asking why their draft showed up on the live site, developers maintaining a spreadsheet of "fields that don't exist in the API yet", permission incidents where a contractor deleted a document they should never have touched, and a content type explosion because nobody modelled the schema — they just kept adding pages.
The right platform enforces discipline through its architecture. Draft isolation, schema-first content, RBAC, versioning, and typed queries are not nice-to-haves. They are the conditions under which a content team can move at speed without breaking production. A web CMS that does not provide all five is asking the developer to build the missing pieces as custom middleware, and that cost never shows up in the vendor's pricing page.