Source Code
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
<div class="container">
<a class="navbar-brand fw-bold" href="javascript:void(0)">Brandly</a>
<div class="collapse navbar-collapse show">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="javascript:void(0)">Home</a></li>
<li class="nav-item dropdown bsml-dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" id="bsmlToggle" data-bs-toggle="dropdown" aria-expanded="false">Products</a>
<ul class="dropdown-menu" aria-labelledby="bsmlToggle">
<li><a class="dropdown-item" href="javascript:void(0)">Dashboard</a></li>
<li class="dropdown-submenu">
<a class="dropdown-item dropdown-toggle" href="javascript:void(0)">Components</a>
<ul class="dropdown-menu dropdown-submenu-menu">
<li><a class="dropdown-item" href="javascript:void(0)">Buttons</a></li>
<li><a class="dropdown-item" href="javascript:void(0)">Forms</a></li>
<li><a class="dropdown-item" href="javascript:void(0)">Modals</a></li>
</ul>
</li>
<li class="dropdown-submenu">
<a class="dropdown-item dropdown-toggle" href="javascript:void(0)">Templates</a>
<ul class="dropdown-menu dropdown-submenu-menu">
<li><a class="dropdown-item" href="javascript:void(0)">Landing Page</a></li>
<li><a class="dropdown-item" href="javascript:void(0)">Admin Panel</a></li>
</ul>
</li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="javascript:void(0)">Pricing</a></li>
</ul>
</li>
<li class="nav-item"><a class="nav-link" href="javascript:void(0)">About</a></li>
</ul>
</div>
</div>
</nav>
<div class="container py-5 text-muted small">Click "Products", then hover or click "Components" or "Templates" to open the nested flyout submenu.</div>.dropdown-submenu { position: relative; }
.dropdown-submenu-menu {
display: none;
position: absolute;
top: 0;
left: 100%;
margin-top: -1px;
}
.dropdown-submenu.show > .dropdown-submenu-menu { display: block; }
@media (max-width: 991.98px) {
.dropdown-submenu-menu { left: 0; top: auto; position: static; }
}const dropdown = document.querySelector('.bsml-dropdown');
const submenus = Array.from(dropdown.querySelectorAll('.dropdown-submenu'));
function closeAllSubmenus(except) {
submenus.forEach(sub => {
if (sub !== except) sub.classList.remove('show');
});
}
submenus.forEach(sub => {
const toggle = sub.querySelector(':scope > .dropdown-toggle');
// Hovering a submenu toggle opens it and closes any sibling submenu that
// was previously open, so only one flyout is ever visible at a time.
sub.addEventListener('mouseenter', () => {
closeAllSubmenus(sub);
sub.classList.add('show');
});
toggle.addEventListener('click', e => {
e.preventDefault();
e.stopPropagation();
const isOpen = sub.classList.contains('show');
closeAllSubmenus(sub);
sub.classList.toggle('show', !isOpen);
});
});
// Closing the top-level Bootstrap dropdown must also reset every submenu,
// otherwise reopening "Products" later would show a stale open flyout.
dropdown.addEventListener('hidden.bs.dropdown', () => closeAllSubmenus(null));
document.addEventListener('click', e => {
if (!dropdown.contains(e.target)) closeAllSubmenus(null);
});Bootstrap Multi-Level Dropdown Menu — Free JS Snippet
Bootstrap Multi-Level Dropdown Menu · Navigation · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Bootstrap Multi-Level Dropdown Menu — HTML, CSS & JavaScript

Bootstrap 5's dropdown component intentionally has no built-in support for nested submenus, so this snippet layers a small, self-contained amount of extra CSS and JS directly on top of the real dropdown-menu/dropdown-item classes rather than reinventing the whole menu system. Each item that should open a flyout is wrapped in a .dropdown-submenu list item containing its own nested <ul class="dropdown-menu dropdown-submenu-menu">; CSS gives .dropdown-submenu position: relative and the nested menu position: absolute; left: 100%, so the flyout is anchored to the right edge of its parent item, and a single .dropdown-submenu.show > .dropdown-submenu-menu { display: block; } rule is the only thing that actually reveals it.
The JavaScript's job is entirely about managing that one show class correctly. Each submenu's toggle link listens for both mouseenter (so hovering opens the flyout, matching the desktop-menu convention users expect) and click (so touch and keyboard users can open it deliberately, with e.preventDefault() and e.stopPropagation() stopping the click from also bubbling up and toggling or closing Bootstrap's own top-level dropdown). Both handlers funnel through closeAllSubmenus(except), which removes show from every submenu except the one being opened — this is the sibling-closing behavior called out in the requirements: without it, hovering from "Components" to "Templates" would leave both flyouts open stacked on top of each other instead of swapping cleanly.
The non-obvious edge case this snippet specifically handles is stale state when the *outer* Bootstrap dropdown closes. Bootstrap's dropdown fires its own hidden.bs.dropdown event on the .dropdown element whenever "Products" closes — by listening for that event and calling closeAllSubmenus(null), every nested flyout is force-reset even if the user never explicitly closed it, so reopening "Products" later never shows a leftover open flyout from the previous visit. A separate document-level click listener also closes every submenu when a click lands outside the whole .bsml-dropdown element, using dropdown.contains(e.target) the same way a standard outside-click-to-close pattern works for any popover or menu.
On narrow viewports the CSS media query collapses .dropdown-submenu-menu back to position: static, so the nested items stack inline underneath their parent instead of trying to flyout sideways off-screen, which would otherwise be unusable on a small screen inside Bootstrap's collapsed navbar.
The toggle lookup inside each submenu uses :scope > .dropdown-toggle rather than a plain querySelector('.dropdown-toggle'), which matters because a submenu's own nested <ul> can itself contain further .dropdown-toggle elements once a third nesting level is added — the :scope > combinator guarantees only the immediate child toggle for that specific submenu is ever wired up, not a descendant belonging to a deeper flyout.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Ask an AI assistant like Claude to add keyboard arrow-key navigation between submenu items, or to support a third nesting level by generalizing the closeAllSubmenus() logic to operate per-parent instead of across the whole top-level dropdown.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a Bootstrap 5.3 navbar with a multi-level dropdown menu using the real Bootstrap CDN (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble Bootstrap.
Requirements:
- A real Bootstrap navbar with a dropdown-toggle nav item using data-bs-toggle="dropdown", opening a standard dropdown-menu.
- At least two items inside that dropdown must themselves open a nested flyout submenu to the side, built as a nested dropdown-menu positioned with CSS (position: absolute, left: 100%) since Bootstrap's dropdown JS has no built-in multi-level support.
- Submenu flyouts must open on both hover (mouseenter) and click, and only one sibling submenu may be open at a time — opening one must close any other open submenu.
- Clicking a submenu toggle must not close the parent top-level dropdown (stop event propagation appropriately).
- All open submenus must reset when the parent dropdown closes (listen for Bootstrap's hidden.bs.dropdown event) and when the user clicks anywhere outside the whole menu structure.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Load the snippetA Bootstrap navbar is shown with Home, Products, and About links.
- 2Click "Products"A real Bootstrap dropdown-menu opens showing Dashboard, Components, Templates, and Pricing.
- 3Hover over "Components"A nested flyout submenu opens to the right, showing Buttons, Forms, and Modals.
- 4Move your mouse to "Templates" insteadThe Components flyout closes automatically and the Templates flyout opens in its place — only one is ever open at a time.
- 5Click a nested item like "Forms"The whole menu structure is a normal link, ready to wire up to real navigation.
- 6Click outside the navbarThe top-level dropdown and any open flyout both close together.
- 7Reopen "Products"It opens fresh with no flyout still expanded from before, confirming the reset logic works.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Yes. Keep an openSubmenuId in useState (React) or a ref (Vue), set it in the mouseenter/click handlers instead of toggling classList directly, and conditionally apply the show class from that state; in Angular, do the same with a component property and *ngClass, calling the reset logic in ngOnDestroy or from Bootstrap's hidden.bs.dropdown event via a ViewChild reference instead of querySelector.
No — Bootstrap 5's dropdown JavaScript only manages a single level of dropdown-menu, so any nested flyout needs the extra positioning CSS and open/close JavaScript this snippet adds on top of Bootstrap's own dropdown-item and dropdown-menu classes.
Without it, a click on a submenu toggle bubbles up to Bootstrap's own dropdown click handling and can immediately close the parent "Products" dropdown at the same time the submenu tries to open, since Bootstrap listens for clicks on and outside its toggle to manage visibility.
closeAllSubmenus(sub) runs on every mouseenter before the new submenu is shown, removing the show class from every other submenu, so Components closes the instant Templates opens rather than both staying visible.
Yes — replace the dropdown-menu/dropdown-item classes with Tailwind's own absolute positioned panel classes and keep the same left-100% flyout positioning and JavaScript open/close logic, since none of it depends on Bootstrap-specific behavior beyond the hidden.bs.dropdown event.
A user can also close the top-level dropdown by pressing Escape or selecting an item, both of which Bootstrap handles internally and fires hidden.bs.dropdown for — listening to that event, rather than only outside clicks, guarantees the submenus reset no matter how the parent dropdown was closed.