Container blocks hold other blocks inside them — sliders with slides, grids with columns, accordions with panels. Define them in your blockSchema using blocks_layout or object_list widgets.
Each child has its own @type and schema (from blocks). Children are stored in a shared blocks dict on the parent, with the field holding { items: [...] } for ordering:
All blocks_layout fields on the same block share the same blocks dict. So a block can have multiple container fields (e.g., header_blocks and footer_blocks) whose children all live in the parent's blocks.
All items share one inline schema, stored as an array with an ID field. Use dataPath when the data is nested within the block:
When allowedBlocks is set on an object_list, items can have different types (like blocks_layout) but are still stored as an array. Each item's type is stored in the field specified by typeField (defaults to '@type') and its schema is looked up from blocks:
Both blocks_layout and object_list look the same in the editing UI and blocks can be dragged between them — data is automatically adapted when moving between formats (ID fields added/stripped, type fields set appropriately).
Add data-block-uid to each child element. You don't need to mark the container element itself:
Set addMode: 'table' for table-like structures (rows containing cells). This lets users add and remove columns as easily as rows:
A container can never be empty. When the last child is deleted, either the defaultBlockType is added, or a special block with @type: "empty" is inserted. Empty blocks are stripped before saving. Render them as empty space — Hydra puts a '+' button in the middle for the user to replace it.
You can override the look of the '+' button by rendering something inside the empty block and adding data-block-add="button" to it.
You might want one container type that holds different block types but constrains them to all be the same type with synchronised settings. A field on the parent lets the editor select the type and all blocks get converted using fieldMappings: