MaterializeCSS modals won’t open? Initialize them in vanilla JavaScript
MaterializeCSS v1 modals need M.Modal.init() after the DOM loads; markup and data attributes alone won't open the overlay.
13 May 2026, 15:33 UTC

The symptom: a trigger that does nothing
You copy the MaterializeCSS modal markup, add a button with class="modal-trigger" and data-target="settings-modal", then click it. Nothing happens. No overlay, no focus trap, no console error. The HTML looks identical to the documentation, so the problem is easy to overlook.
In MaterializeCSS v1, the library is vanilla JavaScript. It does not scan your page continuously, and data-target alone does not wire up click behavior. The modal must be initialized after the DOM is ready.
Worked example: static modal with options
Place this markup near the bottom of your page, before the closing <body> tag:
<a class="modal-trigger" href="#" data-target="settings-modal">Open settings</a>
<div class="modal" id="settings-modal">
<div class="modal-content">
<h4>Settings</h4>
<p>Update your preferences.</p>
</div>
<div class="modal-footer">
<a href="#" class="modal-close waves-effect waves-green btn-flat">Done</a>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelectorAll('.modal');
var options = { dismissible: false, startingTop: '10%', endingTop: '20%' };
M.Modal.init(elems, options);
});
</script>
Where to run: in your browser, on any page that loads MaterializeCSS v1 CSS and JavaScript. No special permissions are required. Replace #settings-modal with your own modal id.
Expected check: clicking “Open settings” shows the overlay and the modal. Because dismissible: false is set, clicking outside the backdrop should not close it.
Risk: if you call M.Modal.init() before the DOM loads, querySelectorAll finds nothing and the modals stay uninitialized.
Programmatic open and close with a null guard
You can control the same modal from other JavaScript. Use M.Modal.getInstance() to retrieve the instance, then call open() or close().
var modalElem = document.querySelector('#settings-modal');
var instance = M.Modal.getInstance(modalElem);
if (instance) {
instance.open();
} else {
console.warn('Modal instance not found. Did M.Modal.init run after the DOM loaded?');
}
getInstance() returns null for elements that were never initialized. Calling instance.open() on null throws a TypeError, so the if (instance) guard is required.
Dynamic modals added after page load
M.AutoInit() is a convenience that initializes matching elements once. It does not scan the DOM again later. If your application inserts a modal with Ajax or a framework, call M.Modal.init() manually on that element after you append it:
var newModal = document.createElement('div');
newModal.className = 'modal';
newModal.id = 'dynamic-modal';
// append modal-content and modal-footer here
document.body.appendChild(newModal);
M.Modal.init(newModal, { dismissible: true });
Limitation: maintenance mode and backdrop verification
MaterializeCSS v1 is largely in maintenance mode. Teams that need active security updates, modern framework bindings, or accessibility improvements should evaluate community forks or alternative UI libraries before starting new projects.
Practical way to check the result: open DevTools while the modal is visible. Confirm the modal element and a backdrop overlay cover the viewport, then run this in the console:
M.Modal.getInstance(document.querySelector('#settings-modal'))
Where to run: Chrome or Firefox DevTools Console while the page is loaded. Permissions: none. Placeholder: replace #settings-modal with your modal id. Expected check: returns an object if initialized, null otherwise. Risk: none; this is read-only inspection.
If it returns null, the init script either did not run or ran before the element existed.
Closing
A MaterializeCSS modal is not openable from markup alone. Initialize it with M.Modal.init() after the DOM loads, guard programmatic calls with a null check, and re-initialize any modals you insert dynamically. That one initialization step is usually the difference between a dead trigger and a working dialog.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.