Hero Block
A full-width hero section with heading, subheading, image, rich text description, and a call-to-action button. Demonstrates multiple field types in a single block: string, textarea, slate, image, and object_browser.
Developer Reference
Schema
Pass this object inside the blocks option when calling initBridge() to register this block type with the admin UI. See Custom Blocks for the full setup guide.
{
"hero": {
"blockSchema": {
"properties": {
"heading": {
"title": "Heading"
},
"subheading": {
"title": "Subheading",
"widget": "textarea"
},
"buttonText": {
"title": "Button Text"
},
"buttonLink": {
"title": "Button Link",
"widget": "object_browser",
"mode": "link",
"allowExternals": true
},
"image": {
"title": "Image",
"widget": "image"
},
"description": {
"title": "Description",
"widget": "slate"
}
}
},
"fieldMappings": {
"@default": {
"title": "heading",
"description": "subheading",
"@id": "buttonLink",
"image": "image"
}
}
}
}JSON Block Data
Example JSON as stored in the Plone content API. This is the data structure your component will receive in the block prop.
{
"@type": "hero",
"heading": "Welcome to Our Site",
"subheading": "Discover amazing content\nacross multiple lines",
"buttonText": "Get Started",
"buttonLink": [
{
"@id": "/getting-started"
}
],
"image": "data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%27800%27 height=%27400%27%3E%3Crect width=%27100%25%27 height=%27100%25%27 fill=%27%234a90d9%27/%3E%3Ctext x=%2750%25%27 y=%2750%25%27 fill=%27white%27 text-anchor=%27middle%27 font-size=%2724%27%3EHero Image%3C/text%3E%3C/svg%3E",
"description": [
{
"type": "p",
"children": [
{
"text": "We build tools that make content editing delightful."
}
]
}
]
}Rendering
How this block renders in your frontend. Add its handling to your renderer, or — for list-style blocks — register a fetcher and reuse your list rendering.
import { getImageUrl } from './utils.js';
function HeroBlock({ block }) {
const subheading = (block.subheading || '').replace(/\n/g, '<br>');
const buttonLink = block.buttonLink?.[0]?.['@id'] || '';
const imageSrc = getImageUrl(block.image);
// Data-driven: render a field only when it has data. No data ⇒ no element, so
// view markup stays clean. Inka reveals an empty optional field for editing by
// seeding it, which makes these same checks true — no edit-mode branch needed.
return (
<div data-block-uid={block['@uid']} className="hero-block">
{imageSrc && (
<img data-edit-media="image" src={imageSrc} alt="Hero image" />
)}
{block.heading && <h1 data-edit-text="heading">{block.heading}</h1>}
{block.subheading && (
<p data-edit-text="subheading" dangerouslySetInnerHTML={{ __html: subheading }} />
)}
{block.description && (
<div className="hero-description" data-edit-text="description">
{block.description.map((node, i) => (
<SlateNode key={i} node={node} />
))}
</div>
)}
{(block.buttonText || block.buttonLink) && (
<a data-edit-text="buttonText" data-edit-link="buttonLink" href={buttonLink}>
{block.buttonText}
</a>
)}
</div>
);
}