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).

The listing example block being edited in Volto Hydra

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.

Build a frontend

The actual code you write will depend on the framework you choose. You can look at these examples to help you:

Button

The button block shows a button for which a link (internal or external) can be stored. The button can be displayed left, right or center and have a background color.

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).

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.

Build a frontend

The actual code you write will depend on the framework you choose. You can look at these examples to help you:

Button

The button block shows a button for which a link (internal or external) can be stored. The button can be displayed left, right or center and have a background color.

Click to add image
Accordion

The accordion contains other blocks in an accordion behavior layout.

Read more
Click to add image
Advanced

Advanced topics for optimising your Inka integration: lazy loading the bridge, authentication, and preventing reloads.

Read more
Click to add image
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.

Read more
Click to add image
Build a frontend

The actual code you write will depend on the framework you choose. You can look at these examples to help you:

Read more
Click to add image
Button

The button block shows a button for which a link (internal or external) can be stored. The button can be displayed left, right or center and have a background color.

Read more
Click to add image
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).

Read more
Plone Foundation Logo

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.

{
  "listing": {
    "itemTypeField": "variation",
    "schemaEnhancer": {
      "inheritSchemaFrom": {
        "mappingField": "fieldMapping",
        "defaultsField": "itemDefaults",
        "filterConvertibleFrom": "@default",
        "title": "Item Type",
        "default": "summary"
      }
    }
  },
  "summary": {
    "fieldMappings": {
      "@default": {
        "@id": "href",
        "title": "title",
        "description": "description",
        "image": "image"
      }
    },
    "blockSchema": {
      "properties": {
        "href": {
          "title": "Link",
          "widget": "url"
        },
        "title": {
          "title": "Title"
        },
        "description": {
          "title": "Description",
          "widget": "textarea"
        },
        "image": {
          "title": "Image",
          "widget": "url"
        },
        "date": {
          "title": "Date",
          "widget": "date"
        }
      }
    }
  },
  "default": {
    "fieldMappings": {
      "@default": {
        "@id": "href",
        "title": "title",
        "description": "description"
      }
    },
    "blockSchema": {
      "properties": {
        "href": {
          "title": "Link",
          "widget": "url"
        },
        "title": {
          "title": "Title"
        },
        "description": {
          "title": "Description",
          "widget": "textarea"
        }
      }
    }
  }
}

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",
  "variation": "summary",
  "querystring": {
    "query": [
      {
        "i": "portal_type",
        "o": "plone.app.querystring.operation.selection.any",
        "v": [
          "Document"
        ]
      }
    ],
    "sort_on": "effective",
    "sort_order": "descending"
  }
}

{
  "@uid": "item-1",
  "@type": "summary",
  "href": "/news/my-article",
  "title": "My Article",
  "description": "Article summary text",
  "image": "/news/my-article/@@images/image-800x600.jpg"
}

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>
  );
}