Sanity CMS Development
Sanity CMS development, where the schema and the query decide everything else.
You’ve already chosen Sanity. What’s still open decides whether the project ages well. Documents have to be shaped for editors who aren’t developers, GROQ has to be written for a render layer that stays fast under load, and Portable Text obliges you to build a renderer on every surface that displays a word of it.
If you’re still comparing content platforms, or scoping the application around the CMS, start at our Next.js web development service instead. Everything below assumes Sanity is settled.
Engineering position
Six things a dataset makes hard to change later.
Shape the document twice: once for the editor, once for the render layer
Editors want few fields, plain names, sensible defaults and a preview that identifies a document without opening it. A render layer wants narrow types it can switch on. The two pull apart in the page-builder array first. Thirty block objects turn every route into a union the front end defends forever, and two of those thirty are hero variants that differ by a background colour. Content belongs in the schema, layout in the front end.
GROQ projections are the whole performance story on a content-heavy route
The spread operator inside a projection returns every attribute of every matched document, so a listing that wants a title, a slug and an image ships the full Portable Text body of each entry. Filter, order, slice, then project explicitly, dereferencing with the arrow only into a sub-projection that names its own fields. Query length matters too. Past a point the client falls back to POST, POST is not cached, and a route you thought was a CDN read now hits the live Content Lake every time.
Portable Text is a serialisation contract, and annotations are where it breaks
Portable Text is an array of blocks. Each block holds children spans, each span holds marks, and a mark is either a simple decorator or a key pointing into that block’s markDefs. A renderer that assumes marks carry their own data handles bold and italic perfectly and drops every internal link. Give the components map an explicit fallback, because the default for a forgotten type is to render nothing, and nothing looks identical to an empty field.
Webhook revalidation has a failure mode nobody documents
The standard wiring is a GROQ-powered webhook that fires on a change and revalidates a tag. It’s wrong twice over. A webhook tells you which document changed, not which pages render it, so renaming an author leaves two hundred posts showing the old byline. Tag by type as well as by id, and resolve the affected routes with a reverse-reference query. Worse, the webhook can arrive before the API CDN has the new document, so revalidating through the CDN re-caches the exact thing you were replacing.
Sanity Studio bends a long way, and stops at the server
Structure builder replaces the default document lists with panes matching how your team works, input components swap in where a plain string field misrepresents the data, and document actions bend to a real editorial process. What stops is the server. No hook runs before a document is written and no beforeChange enforces an invariant for every client, so validation is advisory to anything holding a token. Access control is granular on the higher plans and coarse below them, so settle the plan before you settle the model.
Sanity or Payload: we ship both, so neither answer protects us
Sanity wins when several people edit the same content at once, when nobody wants to operate a database or a CMS server, and when one body of structured content feeds a website, an app and an email system. Payload wins when content and application data belong in one Postgres database, when hosting has to be self-managed, or when access rules have to be enforced server-side. Our Payload CMS development page argues the other side in the same detail.
How it runs
The four promises, and the things that change the price.
Your repo, your Sanity project, from commit one
The Studio, the schema, the query layer and the decision log all live in your GitHub organisation, and the Sanity project sits under your billing rather than ours.
A staging URL by the end of week one
The Studio is deployed and clickable before the schema is finished, because arguing about a document type goes faster when an editor can open it. A build and a written note every Friday.
Decisions in writing
Document type against embedded object, reference against inline, which custom Portable Text types exist and what renders them, and how revalidation works out which routes are affected.
Fixed scope, quoted before kickoff
Change requests are priced and confirmed in writing before anyone starts them. A content model grows a tail of small discoveries, and that tail gets quoted rather than absorbed.
We build with Sanity. We’re not a Sanity partner agency, and nothing on this page will imply we are. Judge us on the reasoning above and on how we document the builds we publish.
Pricing follows the published rate card. Marketing sites start at $7,500, platforms at $28,000, and the minimum for a build is $7,500. Those figures are estimates in US dollars, not quotations; the quote that follows scoping is fixed in writing before kickoff. What moves it, roughly in that order, is how many document types are genuinely distinct, how many surfaces need a Portable Text renderer, and whether the content you have gets parsed rather than copied. The argument we make about what an MVP actually costs applies here too. The estimate follows the decisions, not the page count.
If the site is live and the complaint is speed rather than structure, read why your Next.js site is slow before you commission a replatform. Otherwise send us the content you have and the shape you want, and get a fixed quote against a written schema.
Settle the content model before the first document type.
Send the content you have and the editing experience you want. You’ll get a proposed document model back, with the query layer and the Portable Text contract written beside it. Fixed scope, fixed price.