WE Mega Menu 8.x-1.17 was published on drupal.org on 22 September 2026. Its release notes read "Support Drupal 11 & Drupal 12", and its info file declares core_version_requirement: ^10.3 || >= 11. The release before it, 8.x-1.16, came out in August 2023 and declared ^9.4 || ^10.
We maintain the module. This is the record of what changed between those releases: what the Project Update Bot proposed, what static analysis missed, and where we did not take the automated patch as it was. Everything below can be checked against the commit history on git.drupalcode.org and the issues linked from each section.
What changed, in short
- Version constraint.
^9.4 || ^10in 8.x-1.16,^10 || ^11on the development branch from January 2025,^10.3 || >= 11in 8.x-1.17. - A database call that broke on Drupal 11.
Merge::key()was called with an array; it now callsMerge::keys(). - Hooks in a class. The module's hooks moved to
Drupal\we_megamenu\Hook\WeMegamenuHookswith#[Hook]attributes, and the procedural functions became#[LegacyHook]shims. - A deprecated cache call.
invalidateAll()on the render cache becamedeleteAll(). - Front-end libraries. The admin UI no longer loads Bootstrap 3.3.7 or bootstrap-notify; it loads Bootstrap 5.3.3, and the bundled jQuery UI and Chosen were updated.
- Tooling. The ddev-drupal-contrib add-on, a
phpcs.xml.distwith the Drupal standard, and Upgrade Status as a development dependency.
The Drupal 11 break that static analysis missed
In March 2024 the Project Update Bot opened #3435602, Automated Drupal 11 compatibility fixes. Its patch changed a single line, the core_version_requirement, because Upgrade Status (4.1.0 in that bot run) found nothing else to fix. On paper, the module was ready for Drupal 11.
It was not. The module stores each menu's layout with a merge query in WeMegaMenuBuilder::saveConfig(), and in 8.x-1.16 that query began like this:
$result = \Drupal::service('database')
->merge('we_megamenu')
->key([
'menu_name' => $menu_name,
'theme' => $theme,
])Merge::key() takes one field name and one value. Passing an array is a Drupal 7 habit that core kept working through a shim, and change record 2205327 notes that "beginning with Drupal 10.2, the use of Merge::key() with an array of multiple values will cause a deprecation warning". In Drupal 10.2 to 10.6 that warning is raised by trigger_error() inside the method, and only when an array is passed. The method itself has no @deprecated tag. Upgrade Status runs PHPStan at level 0 with deprecation rules, so it sees a call to a method that exists and has nothing to report.
Drupal 11 removed the shim, and key() now begins with assert(is_string($field)). Saving a menu fails. #3536908, opened in July 2025, reports it from a Drupal 11 site as TypeError: Cannot access offset of type array on array in Drupal\Core\Database\Query\Merge->key(), or as an AssertionError where assertions are enabled. The fix is one word:
->keys([
'menu_name' => $menu_name,
'theme' => $theme,
])That fix reached the 8.x-1.x branch on 2 January 2025, in the same commit that declared ^10 || ^11 (#3493458), and ships in 8.x-1.17. The bot's info file patch alone does not include it; that is the error #3536908 reports. What we take from it:
- A runtime deprecation shows up in the log only when its code path runs. Exercise every save and submit path of a module on the old major version, with deprecation logging on, before you trust a clean Upgrade Status report.
- Before you patch a contributed module that fails on a new major version, check its development branch and issue queue: the fix may already be committed.
From Bootstrap 3 to Bootstrap 5
The admin screen that builds the mega menu shipped Bootstrap 3.3.7, which #3482573 reported in October 2024 as no longer supported for security. Moving Bootstrap forward also meant replacing bootstrap-notify, the plugin behind the admin messages, which #3493458 describes as unmaintained and not working with Bootstrap 5. The messages are now a Bootstrap toast in the admin template.
In 8.x-1.17, we_megamenu.libraries.yml loads Bootstrap 5.3.3 from cdn.jsdelivr.net as an external asset: the CSS and the JavaScript bundle for the admin library, and the CSS for the front-end library. The bundled copies of jQuery UI and Chosen moved from 1.12.1 to 1.14.1 and from 1.6.2 to 1.8.7 (commit d533e47).
One consequence for site owners: pages that show a mega menu now request bootstrap.min.css from jsDelivr. If your Content Security Policy limits style-src, or your site must not load third-party assets (for example for GDPR), allow that host or serve a local copy through your theme's libraries-override.
Drupal 12: hooks move into a class
For Drupal 12 the bot opened #3604953 in June 2026, with a merge request generated by Upgrade Status 5.0.0-alpha2 and Drupal Rector 1.0.0-beta2, refreshed in July with 5.0.0-alpha3 and Rector 1.1.1. Most of it converts the module's hook implementations to the object-oriented form introduced in Drupal 11.1 (change record 3442349): a class in the module's Hook namespace, with a method for each hook, marked with the #[Hook] attribute.
namespace Drupal\we_megamenu\Hook;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Hook\Attribute\Hook;
class WeMegamenuHooks {
#[Hook('entity_presave')]
public function entityPresave(EntityInterface $entity) {
// When a menu is saved, flag its mega menu configuration for an update.
}
}That covers hook_theme(), the block view alter hook for the mega menu block, and the entity insert, delete and presave hooks. The procedural functions stay in we_megamenu.module, reduced to a call to the service and marked #[LegacyHook]:
#[LegacyHook]
function we_megamenu_entity_presave(EntityInterface $entity) {
\Drupal::service(WeMegamenuHooks::class)->entityPresave($entity);
}This is the backwards-compatible pattern the change record describes. Drupal 11.1 and later discover the class, and skip any function marked #[LegacyHook], so nothing runs twice. Earlier versions do not know about attribute hooks, and PHP ignores the attribute, so they call the procedural function, which hands over to the same method. For that hand-over to work on those versions the class has to be a service, which is why 8.x-1.17 adds a we_megamenu.services.yml that registers WeMegamenuHooks with autowire: true; from Drupal 11.1 core registers classes in the Hook namespace on its own. The change record expects support for procedural hooks to be removed in Drupal 13 at the earliest, and the shims and the service entry can go then.
The theme preprocess functions were not part of the bot's conversion and are still procedural in 8.x-1.17. In its July run the bot reported that, according to Upgrade Status 5.0.0-alpha3, its changes make the module compatible with Drupal 12.
Where we changed the bot's patch
We took the conversion, then changed it before tagging the release, in commit 68747ab.
- The version constraint. The bot set
core_version_requirement: ^11.2 || ^12, which leaves out every Drupal 10 site. 8.x-1.17 declares^10.3 || >= 11instead, so a site on Drupal 10.3 or later can install the same release before and after its core upgrade. - The cache call. The module flushes the render cache after a menu is saved. 8.x-1.16 called
invalidateAll(), which is deprecated in Drupal 11.2 and removed from Drupal 12 (change record 3500622). The bot wrapped the call inDeprecationHelper::backwardsCompatibleCall(), to usedeleteAll()from 11.2 andinvalidateAll()before it.deleteAll()exists on every version the module supports, so 8.x-1.17 calls it directly.
function we_megamenu_flush_render_cache() {
$renderCache = \Drupal::service('cache.render');
$renderCache->deleteAll();
}The same commit renamed the module in its info file from "Mega Menu for Drupal 9/10" to "Mega Menu". A later commit ran phpcbf with the Drupal coding standard over the module, and another adopted the ddev-drupal-contrib add-on, which brings phpcs, phpstan, phpunit, eslint and stylelint commands to the repository. Its development environment moved from PHP 8.4 to PHP 8.5, the version the Drupal 12 pre-release requires.
What ^10.3 || >= 11 means
Drupal reads core_version_requirement as a Composer version constraint, and drupal.org publishes it as the release's core compatibility. ^10.3 allows 10.3.0 up to, but not including, 11.0.0. >= 11 allows 11.0.0 and every version after it, with no upper bound, so Drupal 12 falls inside it, and so will later major versions until a release says otherwise.
The release is marked compatible with Drupal 12. Drupal 12.0.0 is not out yet: on 26 September 2026 it is at 12.0.0-alpha1, with 12.0.0 planned for the week of 7 December 2026. If you try 8.x-1.17 on the Drupal 12 pre-release, report what you find in the issue queue.
Other fixes in 8.x-1.17
- Menus made of links defined in code rather than as menu link content, such as the menus core itself provides, did not render: those links have no derivative ID, and the builder now falls back to the plugin ID (commit cd8806d).
- The mobile menu toggle no longer has to be a link: any element with the
navbar-toggleclass in the mega menu region works (commit 6b7cf80). - The alignment of the menu in the admin builder was fixed (commit f873e27).
If you run WE Mega Menu
- Update with Composer,
composer require 'drupal/we_megamenu:^1.17', then rundrush updatedbanddrush cache:rebuild. The release adds no database updates of its own. - If you applied the info file patch from #3435602, or the
keys()patch from #3536908, remove them from your Composer patches first: both changes are in the release, and the patches will not apply to it. - If you ran
8.x-1.x-devto get Drupal 11 support, move to the tagged release. - Check your Content Security Policy for cdn.jsdelivr.net, as above.
What this means for your own upgrade
A module that Upgrade Status reports as compatible can still fail at runtime, and the failure can sit in contributed code you did not write. When we run a Drupal upgrade to Drupal 11 and 12, the site is upgraded on staging and your team checks the pages and journeys that matter there before go-live, which is where a failure like this one shows up. Our Drupal 10 to 11 upgrade checklist lists the other checks, and Drupal end-of-life dates has the dates and PHP versions for each major release. The module has its own page: WE Mega Menu.