Containers and Blocks

A container is a named slot that holds things; a block is a class plus a template that renders HTML.

Syntax<referenceContainer name="content"><block class="..." name="..." template="..." after="..."/></referenceContainer>

Two node types carry the page. A container is a named slot. It renders nothing of its own beyond an optional wrapping tag, and its job is to hold children: content, content.aside, sidebar.main, page.top, footer. A block is a PHP class with a template; it produces the HTML.

Adding to what already exists

<referenceContainer name="x"> and <referenceBlock name="x"> create nothing. They point at an element declared elsewhere, usually in a core module, and everything nested inside them is added to it. This is how a module changes a page it does not own.

Ordering

before and after name a sibling inside the same parent. The literal - is special: before="-" means first, after="-" means last. Neither reference node accepts before or after itself, so position the child, or change the parent with <move>.

Shorthand and longhand

<block class="..." name="acme.help" template="Acme_Delivery::help.phtml"/>

<block class="..." name="acme.help">
    <arguments>
        <argument name="template" xsi:type="string">Acme_Delivery::help.phtml</argument>
    </arguments>
</block>

Those two are the same thing. Both forms appear in core code.

What trips people up

A container only exists if the active page layout declared it: sidebar.main is absent from a one-column page, and a block added to a missing container never renders. Run bin/magento cache:flush after the edit.

Example

Example · xml
<!-- app/code/Acme/Delivery/view/frontend/layout/catalog_category_view.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="sidebar.main">
            <container name="acme.help" label="Category help"
                       htmlTag="div" htmlClass="acme-help" before="-">
                <!-- template as an attribute -->
                <block class="Magento\Framework\View\Element\Template"
                       name="acme.help.returns"
                       template="Acme_Delivery::category/returns.phtml"/>
                <!-- the same thing, written as an argument -->
                <block class="Magento\Framework\View\Element\Template" name="acme.help.contact">
                    <arguments>
                        <argument name="template" xsi:type="string">Acme_Delivery::category/contact.phtml</argument>
                    </arguments>
                </block>
            </container>
        </referenceContainer>
    </body>
</page>

When to use it

  • Put a returns-policy block at the top of the category sidebar.
  • Wrap two blocks in a div with a class so the stylesheet can target them.
  • Add a tab to the product details section without editing the core template.
  • Reuse one block class with two templates on two pages.

More examples

A wrapper container that only exists in configuration

htmlTag plus htmlClass makes the container emit <div class="acme-badges"> around its children, which saves writing a wrapper template. htmlTag is required if you set htmlClass or htmlId. The ifconfig attribute hides the second block unless the configuration path acme_delivery/badges/warranty is on, so a merchant can switch it off without a deployment.

Example · xml
<!-- app/code/Acme/Delivery/view/frontend/layout/catalog_product_view.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="product.info.main">
            <container name="acme.badges" htmlTag="div" htmlClass="acme-badges"
                       after="product.price.tier">
                <block class="Magento\Framework\View\Element\Template"
                       name="acme.badge.returns"
                       template="Acme_Delivery::product/badge-returns.phtml"/>
                <block class="Magento\Framework\View\Element\Template"
                       name="acme.badge.warranty"
                       template="Acme_Delivery::product/badge-warranty.phtml"
                       ifconfig="acme_delivery/badges/warranty"/>
            </container>
        </referenceContainer>
    </body>
</page>

Naming children for a parent template

name is unique across the page; as is the alias the parent uses to fetch the child, which is why core templates can call getChildHtml('acme_delivery'). The group attribute puts the block into the detailed_info group that the product details block renders as tabs, the same mechanism product.info.description and product.attributes use.

Example · xml
<!-- app/code/Acme/Delivery/view/frontend/layout/catalog_product_view.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product.info.details">
            <block class="Magento\Framework\View\Element\Template"
                   name="acme.delivery.tab"
                   as="acme_delivery"
                   group="detailed_info"
                   template="Acme_Delivery::product/tab.phtml">
                <arguments>
                    <argument name="title" translate="true" xsi:type="string">Delivery</argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

The containers you can target on a two-column page

The core file, unmodified. It shows why sidebar.main is missing on one-column pages: the name is created here, in the page layout, not in any page configuration file. When a block refuses to appear, open the page layout for the wireframe the page uses and check the container name exists.

Example · xml
<!-- vendor/magento/module-theme/view/frontend/page_layout/2columns-left.xml -->
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd">
    <update handle="1column"/>
    <referenceContainer name="columns">
        <container name="div.sidebar.main" htmlTag="div" htmlClass="sidebar sidebar-main" after="main">
            <container name="sidebar.main" as="sidebar_main" label="Sidebar Main"/>
        </container>
        <container name="div.sidebar.additional" htmlTag="div" htmlClass="sidebar sidebar-additional"
                   after="div.sidebar.main">
            <container name="sidebar.additional" as="sidebar_additional" label="Sidebar Additional"/>
        </container>
    </referenceContainer>
</layout>

Discussion

  • Be the first to comment on this lesson.