Content Modeling for Headless CMS
Content Modeling Is Schema Design, Not Page Design
Content modeling is the practice of defining the structured fields, relationships, and reusable types that make up your CMS's content — independent of how any particular page lays them out visually. Get this wrong early and every future page type requires a migration; get it right and new page types can often be assembled entirely from existing content types without a schema change. The discipline borrows directly from database normalization: identify entities, their attributes, and their relationships before thinking about layout.
Core Patterns: Pages, Blocks, and Shared Entities
A workable content model for a marketing site typically separates into three layers:
1. Shared entities: things referenced across many pages — testimonials, team members, logos, pricing plans, blog authors. Model these once, reference them everywhere.
2. Content blocks: reusable, typed sections (hero, FAQ accordion, feature grid, CTA banner) that compose into pages. Each block has its own schema and can appear on any page type.
3. Page types: the top-level entity (landing page, blog post, case study) that holds metadata (slug, SEO fields, publish date) and an ordered array of block references.
This separation is what lets a marketer reuse the same testimonial across twelve different landing pages, with a single edit propagating everywhere, instead of twelve copy-pasted text blocks drifting out of sync.
// Sanity schema definition for a reusable testimonial entity,
// referenced (not duplicated) across any number of page blocks.
export default {
name: "testimonial",
title: "Testimonial",
type: "document",
fields: [
{ name: "quote", title: "Quote", type: "text", validation: (r: any) => r.required().max(280) },
{ name: "authorName", title: "Author Name", type: "string" },
{ name: "authorTitle", title: "Author Title/Company", type: "string" },
{ name: "authorPhoto", title: "Author Photo", type: "image" },
{
name: "tags",
title: "Tags",
type: "array",
of: [{ type: "string" }],
description: "e.g. 'enterprise', 'marketing', 'self-serve' — used to filter which testimonials surface on which page",
},
],
};Model for Query Patterns, Not Just Storage
Versioning and Localization Considerations
Two dimensions complicate content models as sites scale: localization (does every field need a translated variant, or only user-facing copy — SEO slugs and image assets usually shouldn't be duplicated per locale) and versioning/drafts (can an editor save a draft without affecting production, and can you roll back a bad publish). Most headless CMS platforms have native locale and draft/publish support, but the content model needs to explicitly mark which fields are localized versus shared, or you end up with inconsistent per-locale drift on fields that should have stayed global (like an internal analytics tracking ID).
Avoiding the Two Common Anti-Patterns
- The freeform rich-text catch-all: a single 'body' field with embedded components inside rich text. Flexible in the moment, but impossible to validate, query, or restyle consistently later — every page becomes a one-off.
- The over-normalized model: breaking every conceivable attribute into its own referenced entity, resulting in editors needing to click through six nested reference panels to change a single CTA button's text. Normalize relationships that are genuinely reused; don't normalize attributes that only ever belong to one parent.
Schema Changes Are Migrations, Budget for Them
What's Next?
With a solid content model underpinning the CMS, the next question is how the frontend consuming this structured content stays consistent and reusable across every page it renders.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.