Like in Magento1, the Topmenu html structure is still pretty hardcoded.

To change the html markup of the submenus in 2.0, I added a plugin which altered the getHtml Method of Magento\Theme\Block\Html\Topmenu

This was already pretty hacky since I extended my Plugin from the Topmenu and then called the _getHtml Method from my instance instead of the original Topmenu instance and did not call the $proceed callback function.

Despite not calling $proceed beeing really rude, the only alternative I had in mind was setting a preference and overriding the _getSubMenu method. Here is the original plugin class:

namespace Bragento2\Base\Model\Theme\Html\Topmenu;

use Magento\Framework\DataObject;
use Magento\Theme\Block\Html\Topmenu;

class Plugin extends Topmenu
    public function aroundGetHtml(
        Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
            ['menu' => $this->_menu, 'block' => $this]


        $html = $this->_getHtml($this->_menu, $childrenWrapClass, $limit);

        $transportObject = new DataObject(['html' => $html]);
            ['menu' => $this->_menu, 'transportObject' => $transportObject]
        $html = $transportObject->getHtml();

        return $html;

     * Add sub menu HTML code for current menu item
     * @param \Magento\Framework\Data\Tree\Node $child
     * @param string $childLevel
     * @param string $childrenWrapClass
     * @param int $limit
     * @return string HTML code
    protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit)
        $html = '';
        if (!$child->hasChildren()) {
            return $html;

        $colStops = null;
        if ($childLevel == 0 && $limit) {
            $colStops = $this->_columnBrake($child->getChildren(), $limit);

        $html .= '<div class="nav__flyout level' . $childLevel . '"><ul class="container">';
        $html .= $this->_getHtml($child, $childrenWrapClass, $limit, $colStops);
        $html .= '</ul></div>';

        return $html;

Now to the real Problem: In Magento 2.1, the category Items are not anymore added by an Observer, but a plugin on the getHtml Method. Since I also have a Plugin which does not call the $proceed method, the category items are not added anymore (So this won't work anymore and I need an alternative)

Next thing I tried was changing the preference to my own Topmenu block class. But since the plugin, that adds the category items is defined for the original Topmenu class, this also does not work.

To my question: How can I change the html markup of the topmenu's submenus in Magento 2.1?

1 Answer 1


You should use Observer


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="page_block_html_topmenu_gethtml_after">
        <observer name="slava_yurthev_page_block_html_topmenu_gethtml_after" instance="SlavaYurthev\Menu\Observer\Html\Topmenu" />


namespace SlavaYurthev\Menu\Observer\Html;

use \Magento\Framework\Event\Observer;
use \Magento\Framework\Event\ObserverInterface;

class Topmenu implements ObserverInterface {
    public function execute(Observer $observer) {
        $menu = $observer->getData('menu');
        $transportObject = $observer->getData('transportObject');
        $transportObject->setData('html', 'your html');

And you will can render this.

Also You can Define Layout

private $layout;
public function __construct(\Magento\Framework\View\LayoutInterface $layout){
    $this->layout = $layout;

And Call Custom Block

$block = $this->layout->createBlock('SlavaYurthev\Menu\Block\Html\Topmenu');
$block->setMenu($menu); // pass menu tree

And replace with html in transport

$transportObject->setData('html', $block->toHtml());
  • Slava, if you would please, what files are the last three code blocks?
    – Krista K
    Jul 30, 2019 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.