Listing
Displays a list of content items from a query. The listing block fetches items from the Plone catalog based on a querystring and renders each item using a configurable item type (variation). Built-in item types are default (title + description) and summary (title + description + image).

Callout
A labelled admonition box — note, tip, warning, or important — with a rich-text body. Use it for asides, gotchas, and warnings inside a page.
Grid Block
A responsive grid layout container. Each cell is a child block (teaser, slate, image, etc.) rendered inside the grid. This is the built-in Volto grid block (gridBlock).
Container Blocks
A block — or the page itself — is divided into regions, and each region holds an ordered list of blocks. Sliders have a slides region, grids have columns, accordions have panels; a page has its main items region (and optionally a header, footer, …).
Context Navigation Block
A vertical navigation list for grouped pages — a left sidebar on desktop and a collapsible disclosure at the top on mobile. Each row is a navItem (hand-added link) and/or a listing (auto-populated from a path query). The active link is detected from the current URL and gets aria-current="page" plus a .current class. Named after Plone's @contextnavigation endpoint, which serves the same purpose.
Accordion
The accordion contains other blocks in an accordion behavior layout.
Advanced
Advanced topics for optimising your Inka integration: lazy loading the bridge, authentication, and preventing reloads.
How Inka Works
Instead of combining editing and rendering into one framework and codebase, these are separated and during editing a two way communication channel is opened across an iframe so that the editing UI is no longer part of the frontend code. Instead a small JS file called hydra.js is included in your frontend during editing that handles the iframe bridge communication to Inka which is running in the same browser window.
Content as Markdown
Author Plone block content as readable Markdown. One loader turns a .md file into block JSON, so the same file is the content the API serves, the deploy ships, and the docs render — with no JSON to hand-edit.
Build a frontend
The actual code you write will depend on the framework you choose. You can look at these examples to help you:
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.
{
"fieldsets": [
{
"id": "default",
"title": "Default",
"fields": [
"variation",
"fieldMapping",
"headline",
"headlineTag",
"querystring"
]
}
],
"properties": {
"fieldMapping": {
"title": "Field mapping"
},
"headline": {
"title": "Headline",
"type": "string"
},
"headlineTag": {
"title": "Headline tag",
"widget": "select",
"choices": [
[
"h1",
"h1"
],
[
"h2",
"h2"
],
[
"h3",
"h3"
],
[
"h4",
"h4"
],
[
"h5",
"h5"
],
[
"h6",
"h6"
]
]
},
"querystring": {
"title": "Search criteria",
"widget": "querystring"
},
"query": {
"title": "Query",
"widget": "querystring"
},
"variation": {
"title": "Item Type",
"widget": "blockTypeSelect",
"filterConvertibleFrom": "@default",
"default": "summary"
}
}
}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": "listing",
"headline": "Listing: Default",
"variation": "default",
"querystring": {
"b_size": "4",
"limit": "10",
"query": [
{
"i": "portal_type",
"o": "plone.app.querystring.operation.selection.any",
"v": [
"Document"
]
},
{
"i": "Subject",
"o": "plone.app.querystring.operation.selection.none",
"v": [
"main folder"
]
}
],
"sort_on": "getId",
"sort_order": "ascending"
},
"styles": {
"backgroundColor": "transparent"
},
"block": "24280e07-e962-4414-8ee5-cdaf58ca5f35",
"query": [],
"headlineTag": "h2"
}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.
function ListingBlock({ block, blockId }) {
const [items, setItems] = useState([]);
useEffect(() => {
async function load() {
const fetchItems = ploneFetchItems({ apiUrl: API_URL });
const result = await expandListingBlocks([blockId], {
blocks: { [blockId]: block },
fetchItems: { listing: fetchItems },
itemTypeField: 'variation',
});
setItems(result.items);
}
load();
}, [block.querystring]);
return (
<div data-block-uid={blockId} className="listing-block">
{items.map((item, i) => (
<BlockRenderer key={i} block={item} />
))}
</div>
);
}







































