Multi-Level Menu Module for 1C-Bitrix: Megamenu, Drag-and-Drop, Caching

Problems with Standard Menus and Our Solution The standard `bitrix:menu` component generates menus from `.menu.php` files or site structure. For simple sites, this is sufficient, but when there are thousands of products in the catalog and you need to manage a megamenu with images, banners, column

Our competencies:

Frequently Asked Questions

Problems with Standard Menus and Our Solution

The standard bitrix:menu component generates menus from .menu.php files or site structure. For simple sites, this is sufficient, but when there are thousands of products in the catalog and you need to manage a megamenu with images, banners, columns, performance suffers. We have developed a module that solves these problems: it provides flexible management through an interface with caching and full megamenu support. The module is suitable for projects with any number of items — tested on catalogs up to 10,000 positions.

Why Standard bitrix:menu Doesn't Handle Large Catalogs Well

One of the main problems is the lack of tagged caching. Each request rebuilds the tree from files, and with dynamic addition of items through the administrative interface, it becomes impossible. Our module stores everything in the database and caches the tree with a tag; invalidation occurs on any change. This yields a 10-50x speed improvement compared to file parsing.

How We Implemented a Scalable Data Model

We used Bitrix ORM (tables b_vendor_menu_*) with adjacency list for hierarchy storage. Why adjacency list instead of nested sets? For menus with frequent structure changes (additions, deletions, moves), adjacency list is faster on write, and reading with caching negates the difference.

Data Model

Module vendor.megamenu:

  • b_vendor_menu_config — menu configurations: id, code, name, site_id, lang, is_active
  • b_vendor_menu_item — menu items: id, menu_id, parent_id, sort, title, url, url_type (absolute/relative/component), target, icon_id, image_id, css_class, visibility (all/authorized/unauthorized), condition (JSON), is_active
  • b_vendor_menu_column — megamenu columns for second-level items: id, item_id, title, sort, items (JSON — links without separate records)
  • b_vendor_menu_banner — banners in megamenu: id, item_id, image_id, url, title, sort

Menu Item Tree

Items are stored in adjacency list (parent_id). To build the tree, iterative traversal is used to avoid recursion in PHP:

class MenuTreeBuilder { public function build(int $menuId): array { $items = MenuItemTable::getList([ 'filter' => ['MENU_ID' => $menuId, 'IS_ACTIVE' => 'Y'], 'order' => ['PARENT_ID' => 'ASC', 'SORT' => 'ASC'], ])->fetchAll(); $tree = []; $index = []; foreach ($items as $item) { $item['children'] = []; $index[$item['ID']] = &$item; } foreach ($index as &$item) { if ($item['PARENT_ID']) { $index[$item['PARENT_ID']]['children'][] = &$item; } else { $tree[] = &$item; } } return $tree; } } 

The tree is fully cached with the tag menu_{code}_{lang}. Invalidation occurs on any change to an item or banner of that menu.

Item Visibility

Menu item visibility conditions:

  • visibility = 'authorized' — item hidden for guests
  • visibility = 'unauthorized' — item hidden for logged-in users
  • condition (JSON) — arbitrary conditions: user groups, region, request parameters
class VisibilityChecker { public function isVisible(array $item): bool { global $USER; if ($item['VISIBILITY'] === 'authorized' && !$USER->IsAuthorized()) return false; if ($item['VISIBILITY'] === 'unauthorized' && $USER->IsAuthorized()) return false; $condition = $item['CONDITION'] ?? []; if (!empty($condition['user_groups'])) { $userGroups = $USER->GetUserGroupArray(); if (!array_intersect($condition['user_groups'], $userGroups)) return false; } return true; } } 

Visibility conditions are applied after retrieving the cached tree — the cache stores the full tree, filtering occurs in memory.

Megamenu with Columns and Banners

For top-level items, you can configure a megamenu dropdown with columns:

[Catalog] ├─ Column 1: Electronics │ ├─ Smartphones │ ├─ Laptops │ └─ Tablets ├─ Column 2: Home Appliances │ ├─ Refrigerators │ └─ Washing machines └─ Banner: [Weekly Deals → /sale/] 

Columns are stored in b_vendor_menu_column, banners in b_vendor_menu_banner. The component generates the HTML structure of the megamenu; styling is done via CSS Grid.

Drag-and-Drop Interface

The administrative interface is built using the Sortable.js library. Items can be dragged to the desired position and parent element. During dragging, an AJAX request updates sort and parent_id in a single transaction.

Active Item

The active menu item is determined by the current URL:

// Exact URL match or path start match function isActive(array $item, string $currentUrl): bool { if ($item['URL'] === $currentUrl) return true; if ($item['URL_TYPE'] === 'section' && str_starts_with($currentUrl, $item['URL'])) return true; return false; } 

The active item and all its ancestors get the CSS class active.

Comparison with the Standard Component

Criterion Standard bitrix:menu Our Module
Admin management Only via files Via visual drag-and-drop
Megamenu Requires manual markup Built-in columns and banners
Caching File-based, no tags Tagged, event-based invalidation
Group visibility Only via condition in file Interface + arbitrary conditions
Implementation 2-3 days setup 10 days turnkey

Work Process

We go through all stages from analysis to deployment:

  1. Analysis — study catalog structure, visibility requirements, integrations.
  2. Design — database schemas, tree prototype, approval with the client.
  3. Development — creation of ORM module, components, admin interface.
  4. Testing — verification on real data, load testing.
  5. Deployment — installation on the live site, caching configuration, training for content managers.

What You Get in the End?

  • Module with ORM tables, a component for menu output, administrative interface.
  • Documentation for setup and use.
  • Source code in a repository.
  • Technical support for 30 days after launch.

Development Timeline

Stage Duration
ORM tables, tree model 1 day
Tree building, caching 1 day
Item visibility conditions 1 day
Megamenu: columns, banners 2 days
Drag-and-drop interface 2 days
Site components (HTML + CSS) 2 days
Testing 1 day

Total: 10 to 15 working days depending on complexity.

Menu Speed OptimizationTo speed up loading, use tagged caching: the component is cached for 3600 seconds with the `menu_{code}` tag. On any change to an item or banner, the cache is automatically cleared via an agent. For large catalogs (10,000+), we recommend enabling Bitrix composite cache and setting up deferred menu loading via `Bitrix\Main\Page\FrameStatic`.

The adjacency list approach is described in the article Adjacency List. Megamenu styling is done using CSS Grid.

Our experience in developing Bitrix modules is over 7 years. We have completed 50+ projects creating custom solutions. We guarantee deadlines and quality. Contact us for an assessment of your task within one day — we will prepare the optimal solution. Get a project consultation: just write to us, and we will discuss the details.