Concepts & Strategy6 min read

Top 5 Reusable Structured Content Components Every Headless CMS Team Should Ship

Every headless CMS team hits the same wall: a "hero" component gets modeled inline on the homepage, then the marketing team wants it on the blog, then on a landing page, and suddenly there are four subtly different hero schemas that all…

Published September 3, 2026

Every headless CMS team hits the same wall: a "hero" component gets modeled inline on the homepage, then the marketing team wants it on the blog, then on a landing page, and suddenly there are four subtly different hero schemas that all drift apart. Nobody can safely change a button style without auditing three teams' content. The failure mode is not a lack of features. It is a lack of reusable, well-modeled structured content components that behave the same everywhere they land.

Sanity is the Content Operating System for the AI era, an intelligent backend that treats content as structured, queryable, and reusable data rather than a pile of page-bound blobs. That framing matters here, because the difference between a component library that scales and one that rots is whether your components are modeled once, referenced everywhere, and queried by shape rather than by page.

This article ranks the five reusable structured content components every team should ship first, from the highest-leverage foundation to the specialized pieces that pay off later. For each, you get the pitch, what it does well, where it fits poorly, and a concrete modeling example you can lift into your own schema.

1. The reusable content block (Portable Text with custom marks)

The single highest-leverage component is a rich text field that is genuinely structured rather than an HTML blob. In Sanity this is Portable Text: rich text stored as an array of typed blocks and spans, with marks and annotations you define yourself. Instead of a WYSIWYG that emits `<div style>` soup, you get a data structure a frontend can render into your design system, and an agent or LLM can read without guessing at layout.

What it does well: portability. The same Portable Text body renders on a website, in a native app, in an email, or into a plain-text summary for search, because the meaning is captured in the data, not baked into markup. Custom annotations let you attach a typed reference to an author, a product, or an internal link as first-class data, so a link to a product is an actual reference you can follow with GROQ's `->` operator, not a fragile href string. Custom marks let editors apply semantic emphasis that maps to your tokens rather than raw bold or italic.

Where it fits poorly: if your team genuinely wants freeform page layout with drag-and-drop positioning, Portable Text is the wrong tool. It models a stream of content, not a canvas. Reach for it when the body is prose with structured insertions, not when the requirement is pixel placement.

Concrete example: define a `blockContent` type once, register a `productReference` annotation, and reuse it as the body field on articles, guides, and product pages. One schema, three content types, zero divergence.

2. The referenced call-to-action (a shared CTA object, modeled once)

The CTA is the component that quietly multiplies. A button with a label, a destination, and a style variant looks trivial, so teams inline it on every page type. Six months later the same three fields exist in nine places, each with slightly different validation, and a rebrand means editing nine schemas.

Model it once as a reusable object type and the problem disappears. Define a `cta` object with a label, a link (which can itself be a union of internal reference or external URL), and a variant enum tied to your design system. Register that object wherever a CTA belongs: inside a hero, at the end of an article, in a promo banner. Because it is one `defineType`, a change to the variant options or the link validation propagates everywhere at once. With Sanity's TypeGen, that schema codegens to a single TypeScript type, so your frontend consumes one `Cta` shape instead of hand-writing nine near-identical interfaces.

What it does well: consistency and governance. Roles and Permissions can gate who edits CTAs that point at paid campaigns, and validation rules keep destinations sane before they ship.

Where it fits poorly: over-abstracting too early. If you truly have one CTA in one place, a shared object adds indirection for no payoff. Extract it the moment the second usage appears, not before.

Concrete example: a `cta` object referenced by both a `hero` object and an `articleFooter` object. When marketing wants a new "ghost" button style, you add one enum value and every surface gets it.

3. The reusable media asset with structured metadata

Images and video are where accessibility and performance debt accumulates. A raw asset upload with no structured metadata means alt text is optional, credits get lost, and crop focal points live in someone's memory. The reusable media component fixes this by wrapping the asset in an object that carries its own typed metadata.

Model a `figure` object: the image asset itself, a required alt text string, an optional caption in Portable Text, an attribution reference, and hotspot and crop data. Sanity's image type stores hotspot and crop coordinates alongside the asset, so the same source image can be responsively cropped per breakpoint on the frontend without re-uploading. Because the asset lives in Content Lake, it is queryable: you can GROQ for every figure missing alt text across the entire dataset in one round trip, which turns an accessibility audit from a manual crawl into a single projection.

What it does well: it makes the right thing mandatory. Validation on the alt field means an editor cannot publish an inaccessible image, and the attribution reference keeps licensing auditable rather than tribal knowledge.

Where it fits poorly: decorative background flourishes that carry no meaning and need no alt text can be over-served by a heavy figure object. Keep a lighter variant for pure decoration.

Concrete example: reuse the `figure` object inside Portable Text as an inline block and as a standalone hero image. One GROQ query, `*[_type == "figure" && !defined(alt)]`, surfaces every gap before launch.

4. The SEO / metadata object (a shared seo type on every page)

Nothing exposes a fragmented content model faster than SEO. When title, description, canonical, and social card fields are scattered across page types, some pages get them, some do not, and no one can query the coverage gaps. The fix is a single `seo` object type that every routable document embeds.

Define `seo` with a meta title, meta description, an optional canonical URL, an Open Graph image (reusing your `figure` object from above), and a no-index toggle. Register it as a field on articles, landing pages, product pages, and category pages alike. Now every page speaks the same metadata vocabulary. Because Content Lake is schema-aware and real-time, a GROQ projection can pull the resolved SEO shape for any route in one request, and you can audit the whole site for missing descriptions with a single filter rather than clicking through the Studio page by page.

What it does well: consistency and measurability. One object means one place to add a field when a new social platform demands a new tag, and one query to prove coverage before a migration.

Where it fits poorly: pages that never appear in a router, like reusable fragments or internal-only blocks, should not carry an `seo` object at all. Attaching it everywhere is as bad as attaching it nowhere.

Concrete example: a `seo` object embedded on `article` and `landingPage`, with a Studio validation warning when the meta description exceeds its recommended length, so editors self-correct before publishing rather than after.

5. The composable page section (an array of blocks for layout)

The last component is the one that ties the rest together: a page assembled from an ordered array of reusable section objects. Rather than a fixed template with hardcoded slots, the page document holds a `sections` array whose members are your reusable objects: hero, feature grid, CTA band, quote, embedded figure, and rich text block. Editors compose pages by adding and reordering blocks, and every block is a component you already modeled.

This is where Sanity's customizable Studio earns its keep. Structure Builder and custom input components let you present that array as a genuine page-building experience inside a React editor you ship, and the Presentation Tool with Visual Editing stitches each block to a live preview so editors see the real rendered page without leaving the editorial loop. Contentful, Storyblok, and others offer block-based composition too, but the Studio is code you own, so the editing surface for your composable page is yours to shape rather than a fixed UI.

What it does well: it gives non-developers safe composition without freeform chaos. Every block is validated, typed, and queryable, so a page is still structured data, not a snowflake.

Where it fits poorly: highly editorial one-off pages sometimes fight a rigid block library. Leave an escape hatch, a raw Portable Text section, for the exceptions.

Concrete example: a `page` document with a `sections` array of type references, queried with a single GROQ projection that resolves each block's shape, so the frontend maps block types to React components one to one.

Ready to try Sanity?

See how Sanity can transform your enterprise content operations.