Dropdowns
A dropdown is a button that opens a small menu of choices: "Sort by", an account menu with Profile and Logout, a list of export formats. Bootstrap's dropdown plugin opens and positions the menu, closes it when you click elsewhere, and lets keyboard users move through the items with the arrow keys.
In this lesson you will build dropdowns, change their direction and alignment, put forms inside them, and control them with options and JavaScript.
A basic dropdown
A dropdown needs a wrapper, a toggle button and a menu:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dropdown</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4">
<div class="dropdown d-inline-block">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Sort by
</button>
<ul class="dropdown-menu">
<li><h6 class="dropdown-header">Price</h6></li>
<li><button class="dropdown-item" type="button" data-sort="low">Low to high</button></li>
<li><button class="dropdown-item" type="button" data-sort="high">High to low</button></li>
<li><hr class="dropdown-divider"></li>
<li><button class="dropdown-item active" type="button" aria-current="true" data-sort="new">Newest first</button></li>
<li><button class="dropdown-item disabled" type="button" aria-disabled="true">Best rated (coming soon)</button></li>
</ul>
</div>
<p class="mt-3" id="sortLabel">Showing: Newest first</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.querySelectorAll("[data-sort]").forEach((item) => {
item.addEventListener("click", () => {
document.querySelectorAll("[data-sort]").forEach((i) => {
i.classList.remove("active");
i.removeAttribute("aria-current");
});
item.classList.add("active");
item.setAttribute("aria-current", "true");
document.getElementById("sortLabel").textContent = "Showing: " + item.textContent;
});
});
</script>
</body>
</html>The pieces:
dropdownon the wrapper gives itposition: relative, so the menu is placed next to the button.- The toggle needs
data-bs-toggle="dropdown"andaria-expanded="false". Bootstrap flipsaria-expandedtotruewhile the menu is open.dropdown-toggleadds the small caret arrow. dropdown-menuis the list; each choice getsdropdown-item. Use<a>items for links to other pages and<button>items for actions on this page.dropdown-headerlabels a section,dropdown-dividerdraws a line,active(witharia-current) marks the current choice, anddisabledgreys out an item.
Try the keyboard: Tab to the button, press Enter or the down arrow to open, use the arrow keys to move, Enter to choose and Escape to close.
Split buttons
A split button has a main action and a separate arrow for more choices, like "Save" with "Save as PDF" and "Save as Excel" in the menu. Put both buttons in a btn-group, and give the arrow dropdown-toggle-split plus hidden text, because it has no visible label:
<div class="btn-group">
<button type="button" class="btn btn-success">Download invoice</button>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">More download formats</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/invoice.pdf">PDF</a></li>
<li><a class="dropdown-item" href="/invoice.xlsx">Excel</a></li>
</ul>
</div>Direction and alignment
By default a menu opens downwards, lined up with the start (left) edge of the button. Change the direction with a class on the wrapper, and the alignment with a class on the menu:
| Class | Where | Effect |
|---|---|---|
dropup | Wrapper | Opens upwards |
dropend, dropstart | Wrapper | Opens to the right or left |
dropdown-center, dropup-center | Wrapper | Menu centred under or above the button |
dropdown-menu-end | Menu | Lined up with the end (right) edge of the button |
dropdown-menu-lg-end | Menu | End-aligned from lg up (needs data-bs-display="static" on the toggle) |
Use dropdown-menu-end for menus near the right edge of the screen, such as an account menu in a navbar, so the menu does not run off the screen. Bootstrap (through Popper) also flips a menu automatically if there is not enough room below it.
Dark menus
In Bootstrap 5.3, add data-bs-theme="dark" to the dropdown (or a parent) to get a dark menu that matches a dark navbar:
<div class="dropdown" data-bs-theme="dark">
<button class="btn btn-dark dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">Account</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/profile">Profile</a></li>
<li><a class="dropdown-item" href="/logout">Log out</a></li>
</ul>
</div>Forms and auto-close
By default a menu closes on any click, including clicks inside it. That is a problem when the menu holds a form or checkboxes. The data-bs-auto-close option on the toggle controls this:
| Value | Menu closes when you click... |
|---|---|
true (default) | Inside or outside the menu |
inside | Only inside the menu |
outside | Only outside the menu (good for forms and filters) |
false | Only on the toggle button or Escape |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Filter dropdown</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4">
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="filterBtn" data-bs-toggle="dropdown"
data-bs-auto-close="outside" aria-expanded="false">
Filter hotels
</button>
<form class="dropdown-menu p-3" style="min-width: 260px" id="filterForm">
<p class="fw-semibold mb-2">City</p>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="jaipur" value="Jaipur" checked>
<label class="form-check-label" for="jaipur">Jaipur</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="kochi" value="Kochi">
<label class="form-check-label" for="kochi">Kochi</label>
</div>
<label for="maxPrice" class="form-label">Max price per night</label>
<select class="form-select mb-3" id="maxPrice">
<option value="1500">₹1,500</option>
<option value="3000" selected>₹3,000</option>
<option value="6000">₹6,000</option>
</select>
<button type="submit" class="btn btn-success w-100">Apply</button>
</form>
</div>
<p class="mt-3" id="summary">No filter applied yet.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
<script>
const toggle = document.getElementById("filterBtn");
document.getElementById("filterForm").addEventListener("submit", (event) => {
event.preventDefault();
const cities = [...document.querySelectorAll("#filterForm input:checked")].map((c) => c.value);
const price = document.getElementById("maxPrice").value;
document.getElementById("summary").textContent = `Cities: ${cities.join(", ") || "any"}, up to ₹${price}`;
bootstrap.Dropdown.getOrCreateInstance(toggle).hide(); // close the menu from code
});
toggle.addEventListener("shown.bs.dropdown", () => console.log("Filter menu opened"));
</script>
</body>
</html>The menu here is a <form> with padding (p-3) instead of a list. With data-bs-auto-close="outside", ticking boxes and choosing a price keeps the menu open; clicking elsewhere on the page closes it. After "Apply", the script closes it with the JavaScript API.
The JavaScript API
const toggleBtn = document.getElementById("filterBtn");
const dd = bootstrap.Dropdown.getOrCreateInstance(toggleBtn);
dd.show();
dd.hide();
dd.toggle();Call the API on the toggle button, not on the menu. Dropdowns fire show.bs.dropdown, shown.bs.dropdown, hide.bs.dropdown and hidden.bs.dropdown on the toggle. The hide events include a clickEvent property, so you can find out which click closed the menu.
Common mistakes
- Using
bootstrap.min.jswithout Popper. Dropdowns throw an error because they need Popper for positioning. Use the bundle file. - A form inside a menu that keeps closing. Add
data-bs-auto-close="outside"to the toggle. - A menu cut off by its container. A parent with
overflow: hidden(such as atable-responsivewrapper) can clip it. Move the dropdown, or setdata-bs-boundaryor a Popper strategy. - A split toggle with no hidden text. Screen readers hear only "button". Add a
visually-hiddenlabel.
Key points
- A dropdown is a wrapper (
dropdown), a toggle withdata-bs-toggle="dropdown"and adropdown-menuofdropdown-items. dropdown-header,dropdown-divider,activeanddisabledorganise the menu.dropup,dropend,dropstartand the-centerclasses change direction;dropdown-menu-endchanges alignment.data-bs-auto-close="outside"keeps a menu open while the user works inside it.bootstrap.Dropdown.getOrCreateInstance(toggle)givesshow(),hide()andtoggle(), and*.bs.dropdownevents report changes.
Exercise
Create a language picker for a news site from Lucknow. It should be a dropup button aligned to the end, labelled "Language: English", with a header "Choose language" and three items: English, Hindi and Urdu. When an item is clicked, update the button text and mark the chosen item as active.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Language picker</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-5 d-flex justify-content-end align-items-end" style="min-height: 300px">
<div class="dropup">
<button class="btn btn-outline-dark dropdown-toggle" type="button" id="langBtn" data-bs-toggle="dropdown" aria-expanded="false">Language: English</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><h6 class="dropdown-header">Choose language</h6></li>
<li><button class="dropdown-item active" type="button" aria-current="true">English</button></li>
<li><button class="dropdown-item" type="button">Hindi</button></li>
<li><button class="dropdown-item" type="button">Urdu</button></li>
</ul>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
<script>
const items = document.querySelectorAll(".dropdown-item");
items.forEach((item) => {
item.addEventListener("click", () => {
items.forEach((i) => { i.classList.remove("active"); i.removeAttribute("aria-current"); });
item.classList.add("active");
item.setAttribute("aria-current", "true");
document.getElementById("langBtn").textContent = "Language: " + item.textContent;
});
});
</script>
</body>
</html>dropup on the wrapper opens the menu upwards and dropdown-menu-end lines it up with the right edge. Clicking an item closes the menu by default, and the script moves the active class and updates the label.
Quick Quiz
1. Which attribute turns a button into a dropdown toggle?
The dropdown plugin listens for data-bs-toggle="dropdown" on the toggle element.
2. A menu contains a small form, but it closes as soon as the user clicks a checkbox. What fixes it?
With outside, clicks inside the menu no longer close it.
3. Which class makes a menu open upwards?
Direction classes (dropup, dropend, dropstart) go on the wrapper element.
4. On which element do you call bootstrap.Dropdown.getOrCreateInstance()?
The dropdown instance belongs to the toggle element.
Learn Bootstrap with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
