Quick answer: A Materialize dropdown is a floating menu attached to a trigger element. Build it in three steps: create a ul with class dropdown-content and a unique id, add a trigger with class="dropdown-trigger" and a matching data-target, then initialize it with M.Dropdown.init(). The two features people miss most are hover: true for mouse-open menus and constrainWidth: false when the menu should be wider than its trigger. The most common failure is a data-target that does not match the ul id.
The complete dropdown in 3 steps
This is the full implementation on Materialize 1.0.0 — trigger, menu, and initialization.
<!-- Step 1: the trigger -->
<a class="dropdown-trigger btn" href="#!" data-target="user-menu">
Actions <i class="material-icons right">arrow_drop_down</i>
</a>
<!-- Step 2: the dropdown menu -->
<ul id="user-menu" class="dropdown-content">
<li><a href="#!">Profile</a></li>
<li><a href="#!">Settings</a></li>
<li class="divider"></li>
<li><a href="#!">Sign out</a></li>
</ul>
<!-- Step 3: initialization (before </body>) -->
<script>
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelectorAll('.dropdown-trigger');
M.Dropdown.init(elems);
});
</script>
Notice the menu is a plain list, not a nested component. That is what makes the dropdown flexible: you can fill the ul with links, dividers, or even an avatar row without changing the initialization. If you call M.AutoInit() instead, every dropdown on the page initializes itself with default options — convenient for learning, less predictable in production.
Dropdown options that matter
| Option | Default | What it does |
|---|---|---|
| alignment | ‘left’ | Which side of the trigger the menu aligns to |
| constrainWidth | true | When true, the menu matches the trigger width |
| coverTrigger | true | Menu covers the trigger or opens below it |
| closeOnClick | true | Closes the menu after a menu item is clicked |
| hover | false | Opens on hover instead of click |
| inDuration | 150 | Open animation, in milliseconds |
| outDuration | 250 | Close animation, in milliseconds |
| onOpenStart | null | Callback fired before the menu opens |
M.Dropdown.init(document.querySelectorAll('.dropdown-trigger'), {
alignment: 'right',
constrainWidth: false,
coverTrigger: false,
hover: true
});
For a top navigation bar this exact configuration is the common one: right-aligned, free-width and hover-opened so the menu feels like a classic desktop menu. Setting constrainWidth: false is essential when menu labels are longer than the trigger — otherwise Materialize clips them to the trigger width.
Opening a dropdown from JavaScript
When the trigger is built dynamically or you need to open the menu programmatically, use the instance API instead of a click:
var instance = M.Dropdown.getInstance(document.querySelector('#user-menu'));
instance.open();
instance.close();
instance.recalculateDimensions(); // call after resizing or changing the trigger
The recalculateDimensions() call is the fix for a menu that points to the wrong place after the page layout shifts — for example when a navbar grows or a responsive breakpoint fires. Navbar placement has its own details in the navbar guide, and dropdowns that must sit inside a slide-out drawer are covered by the sidenav guide.
Nested menus and submenus
Materialize has no built-in submenu component, so a multi-level menu tabs a second ul into the first on hover. The full CSS and markup for that pattern — including the delay that stops the submenu flickering shut — is in the nested dropdown guide. For a mobile accordion, the accordion submenu approach nests collapsible lists instead.
When the dropdown does not open: four culprits
- data-target / id mismatch. The trigger’s
data-targetmust equal theul idcharacter for character. - No initialization. Markup alone does nothing on 1.0.0 —
M.Dropdown.init()orM.AutoInit()is required. - Overflow clipping. A parent with
overflow: hiddencuts the menu off. Materialize appends the menu tobodyunless you set thecontaineroption, but a transformed ancestor still breaks positioning. - Old 0.97.x syntax. The removed jQuery calls from old tutorials fail silently on 1.0.0.
Menus that mix text fields or buttons with dropdown items are worth reviewing against the forms guide and the Materialize CSS tutorial, which cover the input and component basics these menus rely on.
Building a navigation system for a real product and want feedback on the markup? Ampersand Academy reviews front-end layouts one-to-one, using your own project as the curriculum.
Frequently asked questions
Why is my Materialize dropdown not working?
The most common causes are a data-target that does not match the ul id, a missing M.Dropdown.init() call, or an ancestor with overflow hidden that clips the menu.
How do I make a Materialize dropdown open on hover?
Pass hover set to true at initialization: M.Dropdown.init(elems, { hover: true }). The menu then opens on mouse enter instead of waiting for a click.
How do I make a Materialize dropdown wider than its trigger?
Set constrainWidth to false. By default the menu is forced to the trigger width, which clips longer labels; disabling it lets the menu size to its own content.
Does Materialize support nested or submenu dropdowns?
Not out of the box. A submenu is a second ul positioned by custom CSS on hover, which the nested dropdown guide walks through with the delay needed to stop flicker.
How do I open a Materialize dropdown programmatically?
Get the instance with M.Dropdown.getInstance(elem) and call open() or close(). Call recalculateDimensions() after the trigger moves so the menu stays aligned.

