Define custom block types directly in your frontend configuration via the blocks option in initBridge. No Volto plugin deployment required. Each block type needs an id, title, and a blockSchema with its field properties.
initBridge() ReferenceinitBridge(options) opens the iframe bridge and registers your frontend's page and block configuration with the admin. Call it once during page setup when running inside the admin iframe.
page — page-level blocks fieldsDefines the regions of a page where blocks can live. page.schema.properties is keyed by field name; each entry is one region.
Per-field options:
Defaults and side effects:
blocks_layout, it's auto-added with { title: 'Blocks' }.allowedBlocks is auto-restricted (hidden from the BlockChooser globally). To bypass, set the block's restricted to a function instead of true/false.{ items: [] } on load.blocks — block type registryDefines or overrides individual block types. Each key is the block type name (matching what appears in allowedBlocks and @type on saved blocks).
Per-block options (most are passed through to Volto's block config):
'common').true hides the block from the chooser; can also be a function for conditional restrictions.true to use only the schema form in the sidebar (no custom edit component).fieldRules, inheritSchemaFrom, etc. See Schema Enhancers.page and blocks interact via name lookup: a region's allowedBlocks: ['slate', 'slider'] references keys of the blocks registry. You can use one without the other — page alone restricts placement of built-in blocks; blocks alone registers custom types and gets a default blocks_layout region accepting everything.
PATH_CHANGE messages. Use when your frontend embeds state (paging, filters) in URL segments that don't exist on the CMS side. See Listings › Path Transformation.true enables verbose console logging in the bridge. Default false.The Bridge instance, which exposes additional API methods you can call from the frontend (e.g. getAccessToken(), sendBlockUpdate(), sendBlockAction()). See Advanced › Custom Sidebar UI for those.
Child block types (like slide above) must be defined at the top level of blocks. You can also:
restricted: true to hide a block from the block chooser (only usable as child blocks)mostUsed: true to pin a block to the top of the chooserdisableCustomSidebarEditForm: true to use only the schema form in the sidebar (no custom edit component)fieldsets in the schema to organize fields into tabsA `widget: 'slate'` field holds one top-level node. A slate field — like description on the slide above — stores a single paragraph, heading, or list, not a document of several. Pasting or typing multiple paragraphs into it flattens them back into one node; only the built-in slate block splits multi-node content into separate blocks. Design slate fields for single-node content, and use a blocks_layout/object_list of slate blocks when you need several. See Visual Editing › One top-level node per slate field.
Schema enhancers modify block schemas dynamically:
`fieldRules` — add, remove, or conditionally modify field definitions. The value for each rule key can be:
false — always hide the field{ set: { title: '...', widget: '...' } } — always add or replace the field definition{ when: { fieldName: value }, else: false } — show only when condition met{ when: { fieldName: { gte: 2 } }, set: { ... } } — conditional definition override[rule, rule, ...] — switch: first matching rule wins. A bare false in the array is a catch-all hide: [{ when: A }, { when: B }, false] shows on A or B, hides otherwise.'parent.child': false — hide a field inside a widget's inner schemaCondition operators: is, isNot, isSet, isNotSet, gt, gte, lt, lte.
Field paths: ../field for the parent block's field, /field for a page metadata field.
fieldMappings (plural) on a block config defines how fields map between block types. This enables three things:
@default (see Listings).Each key in fieldMappings is either a specific block type name or `@default`.
@default — the canonical content shape@default is a virtual type representing canonical Plone content item fields: @id, title, description, image. These are the same fields that listing query results provide. A block with fieldMappings['@default'] is saying "I can be populated from standard content item fields." The keys in @default must only use these four canonical fields — using other keys (e.g. label, field, required) is invalid and produces a console warning.
Use these when blocks share fields that aren't part of the @default set — for example, facet types sharing { title, field, hidden } or form field types sharing { label, description, required }.
fieldMappings[typeName] always creates a conversion edge.@default only creates edges between types that both have valid @default mappings (keys from { @id, title, description, image }). Types with non-canonical @default keys are ignored.fieldMappings never appear in the "Convert to..." menu.A mapping value is either a string (simple field rename) or { field, type } (rename with type conversion):
When type is specified, the value is converted at runtime:
Type | Conversion |
|---|---|
| Arrays joined with |
| String wrapped as |
| Pass through (expects |
| Non-arrays wrapped in |
| Copied as-is |
When a parent block has mappingField set in its inheritSchemaFrom recipe, the admin sidebar shows a widget that lets editors configure field mappings visually:
@default source fields (@id, title, description, image) on the left.type from the target field definition (e.g. object_browser with mode=link → type: "link").fieldMapping (singular) on the block data.The saved fieldMapping is read at render time by expandListingBlocks — no block registry access needed at render time.
When the editor pastes rich HTML into the page, Hydra will eventually be able to recognise it as a custom block by matching against a CSS selector mapping. The proposed shape:
The css:<selector> key in fieldMappings matches a pasted HTML element; the value maps element attributes to block fields. Not yet implemented — open question on whether this should run via htmlTagsToSlate (bypassing slate conversion) or be encoded into slate so attributes/classes survive.