Custom renderer variables

Magento 2 by default allows rendering child blocks via methods like getChildHtml() and any block via getBlockHtml(). Still, Loki has added various template variables to perform similar tasks: $blockRenderer, $childRenderer and $templateRenderer. This ADR explains why.

$childRenderer

The regular $block->getChildHtml() call allows you to render a block that is a child to the current block. This assumes that the child has been defined in the XML layout and that it has been given a proper name or as argument.

echo $block->getChildHtml('example');

However, if you want to pass data to the child block, it requires a bit more work:

echo $block->getChildBlock('example')->addData(['foo' => 'bar'])->toHtml();

With the $childRenderer, this becomes a tiny bit shorter:

echo $childRenderer->html($block, 'example', ['foo' => 'bar']);

However, underneath some extra tricks are added. The first argument $block is passed on to the child as a property ancestor_block, so that within the child you can refer to the ancestor block as follows (which is actually identical to the Magento core its $block->getParent()):

$block->getAncestorBlock();

There is also an uniq_id property created on the child block, which serves as the base for a DOM ID:

$block->getUniqId();

The layout name of the child is automatically derived from the parent name, so that the hierarchy of XML layout names always makes sense. For instance, if the parent block is called loki.example-parent and the child alias is example-child, the XML layout name of the child becomes loki.example-parent.example-child.

$block->getNameInLayout(); // loki.example-parent.example-child

If a child block is called upon in a loop, the XML layout name is usually suffixed with an incrementing counter.

$block->getNameInLayout(); // loki.example-parent.block2

If the parent block has a property view_model, this ViewModel becomes available to the child as well:

$block->getViewModel(); // for example \Loki\Components\Component\ComponentViewModelInterface

Because of these tricks, the $childRenderer is found frequently in the code. However, there are also cases where $block->getChildHtml() is found to be a better option - mainly, if the data of the parent should not be added to the child to avoid confusion.

$blockRenderer

The $blockRenderer is very similar to the $childRenderer, except it is given a full block name instead:

echo $childRenderer->html($block, 'loki.example-block', ['foo' => 'bar']);

The same variables are added to the child block: view_model, uniq_id and ancestor_block - as explained for the $childRenderer above.

$templateRenderer

The $templateRenderer is very similar to both the $blockRenderer and the $childRenderer, except it is given a template file instead:

echo $templateRenderer->html($block, 'Loki_Example::example.phtml', ['foo' => 'bar']);

The same variables are added to the child block: view_model, uniq_id and ancestor_block - as explained for the $childRenderer above. Just like with the $childRenderer, an alias is generated on the fly as well.

Why need a first argument $block?

In all of the renderer calls, the first argument $block should refer to the block instance that you make the call from. It is cluttering the code a bit, so we tried to remove it and replace it with some other structure. Unfortunately, this failed.

The various renderer classes are added via an observer to each block. They are singletons and therefore should not retain state. We tried this and this caused issues with one child block overriding the parent block data, before a second child block was called. Obviously. Turning these classes into non-singletons (either with shared=true or by using factories) worked perfectly fine. However, this led to 3 objects being created for every block instance and was not very good for performance.

Because of this, we settled for a more explicit syntax with the $block being the first (required) argument for each call.

Last modified: September 23, 2025