Recipes
Build Flexible Pages from Reusable Blocks
Model editor-controlled layouts with stable block contracts.
Blocks work well when editors choose from a bounded design vocabulary and the frontend owns rendering.
Define labeled hero, rich-text, and call-to-action blocks for an editor-controlled page layout.
Use this when
- build a page builder
- let editors arrange page sections
- create reusable content blocks
- model flexible landing pages
Dyrected concepts
blocks, Block, content modeling
Additional packages: No additional packages.
Complete recipe
This is the canonical source compiled and behavior-tested by @dyrected/knowledge.
import { defineBlock, defineBlocksField, defineCollection, defineTextField, defineTextareaField, defineUrlField } from "@dyrected/core";
export const HeroBlock = defineBlock({
slug: "hero",
labels: { singular: "Hero", plural: "Heroes" },
fields: [
defineTextField({ name: "heading", label: "Heading", required: true }),
defineTextareaField({ name: "body", label: "Body" }),
],
});
export const CallToActionBlock = defineBlock({
slug: "callToAction",
labels: { singular: "Call to action", plural: "Calls to action" },
fields: [
defineTextField({ name: "label", label: "Link label", required: true }),
defineUrlField({ name: "url", label: "URL", required: true }),
],
});
export const Pages = defineCollection({
slug: "pages",
fields: [
defineTextField({ name: "title", label: "Title", required: true }),
defineBlocksField({
name: "layout",
label: "Page layout",
blocks: [HeroBlock, CallToActionBlock],
}),
],
});Decisions and cautions
- Keep block slugs stable; they are persisted as
blockType. - Render unknown block types safely during rolling deployments.
- Add new optional fields with compatible defaults and migrate removed/renamed fields deliberately.
- Avoid a block for every cosmetic variation. Model content meaning first and keep layout variants constrained.
Use an array when every item has one shape. Use related collections when entries need independent reuse, permissions, or querying.