Documentation Index
Fetch the complete documentation index at: https://avocadostudioai.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The block system is split across two packages:| Package | Contains | Used by |
|---|---|---|
packages/shared/src/blocks/ | Zod schemas, field metadata, default props | Orchestrator, editor, site, Puck |
packages/blocks/src/blocks/ | React renderers, CSS, tests | Site (SSR), editor (preview), Puck canvas |
How a Block Is Defined
Every block has three parts, co-located in a single file inpackages/shared/src/blocks/:
Schema
The Zod object defines the data shape — field types, enums with defaults, required vs optional, array constraints. This is the source of truth for validation. The orchestrator runsvalidateBlockProps() against this schema before applying any operation.
Metadata (meta)
Rich metadata layered on top of the schema:
fields— per-fieldFieldMetawithkind,label,imageSpec,inlineEditablelistFields— describes array fields (likecardsin CardGrid) with item-level field metadatadisplayName,description,category— used by the editor’s block picker and property panelchrome— iftrue, the block is structurally pinned (e.g. SiteHeader, Footer) and cannot be added, moved, or removed
Default Props
An exported function (e.g.bannerDefaultProps()) that returns sensible starter content. Used when the AI or user adds a new block — the defaults are the starting point that the AI then modifies.
Field Metadata Vocabulary
The_helpers.ts file provides factory functions for declaring field metadata:
kind field drives behavior across the stack:
| Kind | Editor UI | AI planning | Preview overlay |
|---|---|---|---|
text | Text input | Free text generation | Inline editable |
richtext | Textarea | Markdown generation | Inline editable |
url | URL input | Link generation | Not inline editable |
image | Asset Manager modal | Image resolution (Unsplash/DALL-E) | Not inline editable |
imageAlt | Text input | Alt text generation | Inline editable |
enum | Dropdown selector | Constrained to options | Not inline editable |
color | Color picker | Hex color generation | Not inline editable |
number | Number input | Numeric generation | Not inline editable |
headingLevel | Dropdown (h1-h6) | Heading hierarchy | Not inline editable |
How a Block Is Rendered
Renderers live inpackages/blocks/src/blocks/{type}/renderer.tsx:
- Untyped props — renderers accept
Record<string, unknown>and coerce to safe types. Validation happens upstream in the orchestrator. data-editable-target— marks DOM elements for inline editing in the preview overlay. The value matches a prop key.- No imports from
shared(usually) — renderers are stateless view functions. They don’t validate or re-fetch metadata.
How They Connect
Schemas and renderers are joined by type name convention — the string"Banner" passed to registerBlock() must match the key in the renderers map:
SharedBlockRenderer is the glue:
The Registry Singleton
The registry usesglobalThis to ensure a single instance survives Next.js webpack module duplication across RSC, SSR, and API route layers:
registerBlock() in a custom block file would populate a different registry copy than getBlockMeta() reads — blocks would appear registered but metadata would be missing.
Runtime Queries
The registry exposes query functions used across the stack:| Function | Used by | Purpose |
|---|---|---|
getBlockMeta(type) | Editor property panel, Puck config | Get metadata for a block type |
getAllBlockMeta() | Block manifest API, catalogue | Get all registered metadata |
getImageFields(type) | Orchestrator image resolution | Which props are image fields (cached) |
getListImageFields(type) | Orchestrator image resolution | Which array items have images (cached) |
getImageSpec(type, path) | DALL-E/Unsplash requests | Aspect ratio for a field path like cards[0].imageUrl |
isFieldInlineEditable(type, path) | Preview overlay | Can this field be edited in-place? |
isChrome(type) | Orchestrator ops engine | Is this block pinned (header/footer)? |
validateBlockProps(type, props) | Orchestrator ops engine | Run Zod validation before applying ops |
defaultPropsForType(type) | AI planner, ops engine | Seed new blocks with defaults |
getBlockJsonSchema(type) | Block manifest API | Convert Zod to JSON Schema for external consumers |
Block Manifest API
When the editor connects to a site, it fetches the block manifest fromGET /api/editor/blocks. This endpoint serializes registered blocks into a JSON payload the editor and AI planner can consume:
getBlockJsonSchema() converts the Zod schema to JSON Schema and strips validation-only constraints (minLength, required, $schema, additionalProperties) — the editor only needs the structural shape.
For custom blocks (external sites), the manifest is authored directly as JSON Schema in propsSchema.
Full Lifecycle
How-To Guides
Add a new block type
Register the import
Add
import "./my-block.ts" to packages/shared/src/blocks/index.ts so the schema is loaded at startup.Register the renderer
Add
MyBlock to the renderers map in packages/blocks/src/blocks/index.ts and to the RendererBlockType union in block-types.ts.Add styles
Create
packages/blocks/src/blocks/my-block/styles.css and import it from packages/blocks/src/blocks/styles.css.Add a field to an existing block
Update the Zod schema
Add the field to the block’s
z.object() in packages/shared/src/blocks/{type}.ts. Use .optional() if it’s not required.Add field metadata
Add an entry to
meta.fields using the f.* helpers. Choose the right kind — it determines editor UI, AI behavior, and inline editability.Update default props
If the field should have a starter value, add it to the
*DefaultProps() function.Update the renderer
Read the new prop in the renderer component. Add
data-editable-target="fieldName" if it should be inline-editable in the preview.Add a list field (repeatable items)
Add AI guidance for a block
If the AI makes mistakes with your block’s props (wrong enum values, missing cross-field dependencies), add a note inapps/orchestrator/src/nlp/deterministic-planner-suggestions.ts:
- Enum semantics (“use
centertextAlign withfullimagePosition”) - Cross-field dependencies (“full-bleed variant REQUIRES imageUrl”)
- Richtext conventions (which markdown subset is supported)
- Optional field toggle behavior (“omit or set empty to hide”)
Add image fields with AI resolution
Mark image fields withf.image() and include an imageSpec:
imageSpec tells the AI planner what dimensions to request from DALL-E or Unsplash. The imageAlt kind pairs with the image field for accessibility. Both are auto-detected by getImageFields() and getImageSpec() — no additional wiring needed.
For list items with images, declare them in listFields.itemFields: