Pricing
Sign in Get started

Content blocks: composable pages, safely

Knowledge base 5 min read

A field of type blocks turns a record into an ordered list of typed components (hero, feature grid, FAQ, CTA), each with its own sub-fields. Editors compose; your frontend maps type → component.

The stored shape

"content": [
  { "type": "hero", "_v": 1, "heading": "Welcome", "subheading": "…" },
  { "type": "cta",  "_v": 1, "label": "Start", "url": "https://…" }
]

Why it stays safe over time

  • Versioned: every block is stamped with its block-type version (_v). Change a block's fields → bump the version → run blocks:normalize to migrate stored content.
  • Tolerant: a block whose type was removed is preserved, never silently deleted. Render a fallback for unknown types in your frontend and nothing ever crashes a page.
  • Validated: required sub-fields are enforced on every write path: editor, REST and MCP.

Rendering (React sketch)

const registry = { hero: Hero, cta: Cta, faq: Faq };
blocks.map((b, i) => {
  const C = registry[b.type] ?? UnknownBlock;   // tolerant fallback
  return <C key={i} {...b} />;
});

Rule of thumb: keep anything you need to filter or sort by (title, path, status) as a top-level field; blocks are for layout, not queries.

Try it on a real project

Reading is slower than building here. Spin up a workspace and see for yourself.

Get started