What is a Bootstrap dropdown?
Click opens it. Hover never does, and the Bootstrap team calls that an intentional design decision.
Behind the click sits Bootstrap’s JavaScript plugin, with Popper handling position. The list of links or actions only appears because the plugin toggles it.
CSS-only dropdown menus need no script at all, which this component can’t say. The plugin does the toggling, closing and positioning, and Bootstrap adds no ARIA menu roles to the markup.
Required classes and elements
The wrapper is .dropdown, or any element that declares position: relative. A .btn-group works too.
The toggle needs .dropdown-toggle plus data-bs-toggle=”dropdown”. A button is the recommended element. A link works as well, and the docs suggest adding role=”button” to it.
The menu is .dropdown-menu, usually a ul, and each item is a .dropdown-item on an a or button element, one per li.
The plugin opens a menu by toggling the .show class on .dropdown-menu.
Any of the Bootstrap button styles can act as the toggle once .dropdown-toggle is added, in every size.
Menu content: items, headers, dividers and text
Items can be a elements or button elements, whichever matches the action. Around them you can add:
- .dropdown-header on an h6 to label a group of items
- .dropdown-divider on an hr between groups
- .dropdown-item-text for non-interactive text
- .active on the current item, and .disabled to grey one out
Pair .active with aria-current so assistive technology announces the state. Use page for the current page and true for the current item in a set.
The Sass default for menu width is a minimum of 10rem, and the docs advise extra sizing styles when a menu holds free-form text.
How to add a Bootstrap dropdown to a page
Dropdowns require Popper, and bootstrap.bundle.min.js already includes it. So the bundle is the only script you need, placed just before the closing body tag.
- Add the bootstrap.min.css link from jsDelivr to the head (version 5.3.8 as of October 2026).
- Wrap the toggle and the menu in a div with the class dropdown.
- Add a button with the classes btn btn-secondary dropdown-toggle, plus type=”button”, data-bs-toggle=”dropdown” and aria-expanded=”false”.
- Add a ul with the class dropdown-menu, then one li per item with an a that carries dropdown-item.
- Place the script tag for bootstrap.bundle.min.js just before the closing body tag.
- Open the page and click the toggle. The menu shows, and a click outside or the Esc key closes it.
Because the markup is ordinary HTML, any template language or CMS can output it.
The bundle is one file with Popper inside, and it’s the default in the docs quick start. If you’d rather load separate files, popper.min.js goes first (@popperjs/core 2.11.8 in the current docs), then bootstrap.min.js.
Skip Popper and the plugin throws a TypeError (“Bootstrap’s dropdowns require Popper”) when the menu first opens, per the dropdown.js source.
No jQuery is involved. The 5.3.8 CDN instructions list Popper and Bootstrap’s script only.
Projects with a build step can install from npm instead (npm install bootstrap@5.3.8, the current release in October 2026).
Direction and alignment options for the dropdown menu
A class on the parent sets direction, and a class on the menu sets alignment. With neither, the menu opens below the toggle and lines up with its left edge.
For direction, .dropup opens the menu above the toggle, .dropend opens it to the right and .dropstart to the left. There are also .dropdown-center and .dropup-center, which center the menu below or above. In RTL layouts the sides mirror, so .dropstart appears on the right.
Positioning comes from Popper, which reads the available viewport and flips the menu when the preferred side has no room.
| Class | Result | Extra requirement |
|---|
| .dropdown-menu-end | Aligns the menu to the right edge of the toggle | None |
| .dropdown-menu-lg-end | Left-aligned by default, right-aligned from the lg breakpoint | data-bs-display=”static” on the toggle |
| .dropdown-menu-end with .dropdown-menu-lg-start | Right-aligned by default, left-aligned from lg | data-bs-display=”static” on the toggle |
Swap lg for sm, md, xl or xxl to move the switch point. The docs require static display first, because Popper otherwise controls placement.
Inside a navbar the attribute is unnecessary, since Popper is not used there.
Breakpoint variants fit a responsive layout where the toggle sits at the right edge on phones but mid-row on desktop.
Bootstrap dropdown options, methods and events
Every option can be set from markup or from script. Beyond the options, the plugin exposes 7 methods and fires 4 events.
Data attributes
Add data-bs-toggle=”dropdown” to a link or button and the plugin takes over.
Keep the attribute even when you initialize the dropdown from script. Bootstrap relies on it to close open menus at the application level.
Options follow the pattern data-bs- plus the option name in kebab-case, so autoClose becomes data-bs-auto-close and offset becomes data-bs-offset=”10,20″.
Since 5.2.0 there is also an experimental data-bs-config attribute that takes options as a JSON string, and separate data-bs-* attributes override it.
JavaScript API
Call new bootstrap.Dropdown(element) on each toggle, or pass a selector string, and add an options object as the second argument.
The show, hide and toggle methods open, close or flip the menu. update recalculates its position and dispose removes the stored instance data. getInstance and getOrCreateInstance return the instance for an element.
Use plain JavaScript when an option needs a value that attributes cannot carry, like an element for boundary, a function for popperConfig, or an object with getBoundingClientRect for reference.
React, Vue and Angular are the exception. Bootstrap’s docs say its JavaScript is not fully compatible with frameworks that assume full control of the DOM, and point to React Bootstrap, ng-bootstrap and ngx-bootstrap instead.
Options and defaults
Each option has a documented default.
| Option | Default | Accepted values |
|---|
| autoClose | true | true, false, ‘inside’, ‘outside’ |
| boundary | ‘clippingParents’ | string, or an element (JavaScript only) |
| display | ‘dynamic’ | ‘dynamic’, or ‘static’ to turn Popper off |
| offset | [0, 2] | array, string (“10,20” in attributes) or function |
| popperConfig | null | object, or function that receives Bootstrap’s default config |
| reference | ‘toggle’ | ‘toggle’, ‘parent’, an element, or an object with getBoundingClientRect |
autoClose is the one people trip over. With true, any click closes the menu, inside or outside. With false it closes only through the toggle or a hide or toggle call. ‘inside’ closes it only on clicks inside the menu, and ‘outside’ only on clicks outside.
For a menu that holds form fields, set data-bs-auto-close=”outside”, as the docs form example does. The close handler in the 5.3 source also ignores events from input, select, option, textarea and form elements inside the menu.
The docs contradict themselves on one point. The description of false says Esc will not close the menu, while the note in the same table cell says Esc always does. The keydown handler in the 5.3.8 source hides an open menu on Esc whatever autoClose is set to, as long as focus is on the toggle or inside the menu, so the note is the accurate one.
Events
All four events fire on the toggle and then bubble, so one listener on the .dropdown parent covers every dropdown inside it.
show.bs.dropdown and hide.bs.dropdown fire as soon as the show or hide method is called. shown.bs.dropdown and hidden.bs.dropdown wait until the menu is visible or hidden and the CSS transitions have finished.
Methods return before the transition ends, so wait for shown or hidden before reading the menu state. The hide and hidden events also carry a clickEvent property when a click triggered them.
Does the Bootstrap dropdown support keyboard navigation and screen readers?
Partly. The plugin handles keyboard movement and closing, but it adds no ARIA menu roles, so screen reader semantics are left to the author.
Arrow keys move through the .dropdown-item elements and Esc closes the menu. Tab moves focus inside an open menu without closing it (dropdown.js source).
Everything else is on you. That means role=”button” on a link that works as the toggle, aria-current on the active item, and role and aria- attributes if the menu must be a true ARIA menu.
The reason is scope. The ARIA menu role is meant for application-like menus that trigger actions, and it allows only menu items, checkbox items, radio items, radio groups and sub-menus.
A dropdown that holds a login form or a search field cannot qualify. That is why Bootstrap keeps the markup generic.
Older Bootstrap 3 tutorials, W3Schools among them, put role=”menu” on the list. That is valid only when every child is a real menu item and the menu follows the full menu keyboard model.
The W3C’s ARIA Authoring Practices Guide describes that model as the menu button pattern. The toggle gets aria-haspopup (aria-controls is optional), aria-expanded switches between true and false, and Enter or Space opens the menu with focus on the first item.
Bootstrap’s toggle ships with aria-expanded=”false” in the markup, and the plugin keeps the value in sync as the menu opens and closes.
Run a keyboard-only pass and a screen reader pass before release, as part of a web accessibility checklist.
Common Bootstrap dropdown patterns: navbar, split button, form and dark menu
Navbar items, split buttons, form panels and themed menus cover most real projects. Each one changes the markup around .dropdown-menu and leaves the plugin itself alone.
Navbar dropdown
A navbar dropdown keeps the wrapper and the toggle on separate, nested elements. The docs say menus need a wrapping element for positioning, so .nav-item and .nav-link stay separate.
The item is an li with the classes nav-item dropdown. Inside it, the toggle is an a with nav-link dropdown-toggle, role=”button”, data-bs-toggle=”dropdown” and aria-expanded=”false”. The menu is a ul with dropdown-menu, placed in the same li.
Placement works differently here. Bootstrap marks navbar menus with data-bs-popper=”static”, and the compiled Sass switches .dropdown-menu to position: absolute only from the expand breakpoint up, so below that breakpoint the menu sits in the flow of the collapsed panel.
Collapse breakpoints and togglers come from the Bootstrap navbar component, which has its own set of classes.
One limit carries over from Bootstrap’s design. Mark Otto, one of its creators, noted in a 2012 post that a click opens the menu, so the toggle cannot also navigate to a page of its own.
Split button dropdown
The split toggle trims its horizontal padding by 25% and drops the left margin, per the Bootstrap docs. That keeps the caret centered in a smaller hit area.
The markup is a .btn-group holding two buttons, then the menu. The second button carries dropdown-toggle, dropdown-toggle-split and data-bs-toggle=”dropdown”.
Give that button a visually-hidden span reading Toggle Dropdown, because a caret alone gives a screen reader nothing to announce.
A split dropstart is the odd one out. The docs put the toggle and the menu before the main button in the source order, so the menu opens on the left of the main button.
Form inside a dropdown
There are two ways to do it. A form with px-4 py-3 can sit inside .dropdown-menu, with a divider and extra links below it, which is the usual sign-in panel. Or the form itself can carry .dropdown-menu and p-4, which suits a single compact form.
Field styling comes from the regular Bootstrap form classes (form-label, form-control, form-check), so nothing dropdown-specific is needed.
Spacing is the catch. Horizontal menu padding defaults to 0 ($dropdown-padding-x), so a form without px-4 or p-4 touches the menu edges.
Dark menu and styling
Dark menus changed in v5.3.0. The migration guide deprecates .dropdown-menu-dark and replaces it with data-bs-theme=”dark”.
Where the attribute sits decides how far it reaches. On the html element, every component turns dark. On a wrapper, everything inside it does. On the .dropdown itself, only that menu does.
The same attribute replaces .navbar-dark, so a dark navbar and the menus inside it follow one switch.
Since v5.2.0 the menu reads 26 local CSS variables on .dropdown-menu, all prefixed –bs-dropdown-. Bootstrap’s own blue theme example overrides –bs-dropdown-bg and –bs-dropdown-link-active-bg inside a data-bs-theme=”blue” selector. If you work in Sass, variable overrides go after the functions import and before the variables import, per the 5.2.0 migration notes.
One trade-off sits in the color modes docs. Switching the color mode type to a media query makes themes automatic but removes per-component theming.
Can a Bootstrap dropdown open on hover or hold nested menus?
Neither is built in. The docs state that dropdowns toggle on click rather than hover, and the 5.3 dropdown page describes no submenu pattern.
Otto’s 2012 design post gives the history. Before Bootstrap was open sourced the dropdowns fired on hover, and the team switched to click because the menu disappeared the moment the cursor left it.
His rule was simple. Hover indicates, click acts, and opening a dropdown is an explicit action.
A hover rule fails on touch screens, which have no hover. It also leaves aria-expanded at false, since a CSS rule never runs the plugin, and any gap between toggle and menu closes the menu as the pointer crosses it.
Bootstrap leaves a small gap on purpose: the $dropdown-spacer Sass variable is .125rem. Drupal’s bootstrap_barrio theme stylesheet deals with it directly, setting margin-left to 0 on flyout menus so the pointer never crosses a dead zone, and it applies its hover rule only from 768px up.
Otto’s reason and the barrio fix describe the same failure from two sides.
A nested menu is a second .dropdown-menu inside an item, positioned with top: 0 and left: 100% in the barrio stylesheet, plus extra CSS or script to show and hide it.
Most implementations write that logic by hand, and examples of JavaScript menus show the usual approaches.
My advice is to keep top-level toggles click-only and stop at one nested level. If the design really demands hover, add it only from the md breakpoint up. Bootstrap Studio’s multilevel guide also advises regular dropdowns where possible, citing better usability on mobile devices.
Bootstrap 4 versus Bootstrap 5 dropdown differences
Bootstrap 5 namespaced every plugin data attribute, so a toggle that still carries data-toggle gets no behavior. Copy-pasted v4 markup fails quietly rather than loudly.
| Aspect | Bootstrap 4 | Bootstrap 5 |
|---|
| Toggle attribute | data-toggle=”dropdown” | data-bs-toggle=”dropdown” |
| Scripts | jQuery plus Popper.js 1.x | Popper 2.x, no jQuery |
| Side openings | .dropleft, .dropright | .dropstart, .dropend |
| Right alignment | .dropdown-menu-right | .dropdown-menu-end |
| Item markup | div with links | ul and li supported |
| Auto close | Not available | autoClose option |
| Event target | Parent .dropdown | Toggle button, then bubbles up |
The v4 flip option no longer exists. Flipping is plain Popper configuration now, and an empty fallbackPlacements array turns it off, per the migration guide.
The browser floor moved too. Projects that must still run in Internet Explorer 11 hit a cross-browser compatibility wall, because Bootstrap 5 dropped IE 10 and 11 along with Legacy Edge.
Bootstrap 4 has been end of life since January 1, 2023, according to the release schedule in Bootstrap’s twbs/release repository. Its final release, 4.6.2, shipped on July 19, 2022. Bootstrap 5 has been the default version since May 5, 2021, and 5.3.x is the current line as of October 2026.
Bootstrap 6 exists only as a pre-release, v6.0.0-alpha.1, published on October 8, 2026 and tagged next on npm. The alpha.1 notes list renaming the dropdown to menu, and the 6.0 docs create it with new bootstrap.Menu(menuToggleEl) for each element matched by [data-bs-toggle=”menu”].
Vendors are acting on the v4 end of life. DevExpress Blazor removed its Bootstrap v4-based theme stylesheets in release v26.1, following a March 2026 announcement.
Pick 5.3.x for anything shipping today. Treat the v6 alpha as a preview of the rename, not a target.
Why is a Bootstrap dropdown not working or cut off?
Most reports come down to a dead click, a misplaced menu, an unexpected flip upward or a clipped menu. Each has its own cause, and the clipping case has the least obvious fix.
| Symptom | Cause | Fix |
|---|
| Click does nothing | Script missing, Popper missing, or data-toggle used on a v5 page | Load bootstrap.bundle.min.js before the closing body tag and use data-bs-toggle |
| Menu appears far from the toggle | Wrapper does not declare position: relative | Wrap toggle and menu in .dropdown |
| Menu opens upward | Popper flips it when space below runs out | Leave it, or empty fallbackPlacements in popperConfig |
| Menu is cut off | Ancestor with overflow hidden, auto or scroll | Set Popper’s strategy to fixed through popperConfig |
An absolutely positioned menu cannot escape an ancestor that clips overflow. Bootstrap’s issue tracker records the pattern in #24251, which covers large dropdowns becoming unusable inside an overflow scroll container, and responsive wrappers such as Bootstrap tables with .table-responsive are the usual host.
The fix is a popperConfig function that spreads Bootstrap’s default config and sets strategy to “fixed”.
Koha, the open source library system, patched exactly this in Bug 40857 (September 2025). A combobox menu inside a Bootstrap modal scrolled with the modal body, so the patch passes a fixed strategy for modal contexts.
Two Bootstrap sources look like they disagree on the syntax. A merged docs pull request (#42470) sets data-bs-strategy=”fixed” on toggles in the live previews of resizable examples to stop menus clipping, but it fixes the v6 docs, where the Menu component lists a strategy option. The 5.3 options table lists no strategy option.
On 5.3.x, use popperConfig.
Bootstrap dropdown FAQ
How do I show the selected item’s text on the toggle button?
Bootstrap has no built-in select behavior, so a click listener does the job. Copy the clicked .dropdown-item text into the toggle, then move .active and aria-current=”true” to that item so assistive technology hears the choice.
Can a dropdown toggle be disabled?
Yes. Add the disabled attribute to a button toggle, and the docs say Bootstrap applies pointer-events: none to it. A link toggle has no such attribute, so it needs .disabled, aria-disabled=”true” and tabindex=”-1″ to keep keyboard users out.
Why does a dropdown menu appear behind a fixed navbar or modal?
Stacking order decides it. Bootstrap’s z-index scale gives menus 1000, below sticky at 1020, fixed at 1030 and modal at 1055, so a menu that overlaps one of those layers from outside it renders underneath.
Changing $zindex-dropdown alone is risky, because the docs say that altering one value likely means altering them all.
Which setting controls dropdown menu width?
Set the –bs-dropdown-min-width variable on the .dropdown-menu, or change the $dropdown-min-width Sass variable to affect every menu. A menu holding free text also needs a max-width style, as the docs example shows.
Verifying Every Bootstrap Dropdown After an Upgrade
After an upgrade, click each toggle to open it, then close it with Esc and with an outside click. Look at alignment and clipping at the narrowest and widest breakpoints. Last, try the arrow keys, Tab and aria-expanded.
That order follows dependency. A dead toggle makes every later check meaningless, and keyboard behavior cannot be judged until the menu opens reliably.
The downside is manual effort, because each variant (navbar, split button, form, themed menu) needs its own pass.
As of October 2026 this routine targets the 5.3 line, and a stable Bootstrap 6 release would reset it.
The same routine suits other Bootstrap components that depend on Bootstrap’s JavaScript.