
Quick answer: A Materialize modal is a dialog that opens over the page. You build it in three steps: write a div with class modal and a unique id, add a trigger link whose href matches that id, and initialize it with M.Modal.init(). By default the modal closes when the user clicks the dark overlay or presses the Esc key. The three mistakes that break most modals are a trigger href that does not match the id, a missing initialization call, and placing the modal inside a fixed or CSS-transformed parent.
The complete modal in 3 steps
This is the full implementation — markup, trigger and initialization. Paste it into a page that already loads Materialize CSS and JS on version 1.0.0.
<!-- Step 1: the trigger -->
<a class="waves-effect waves-light btn modal-trigger" href="#modal1">Open Modal</a>
<!-- Step 2: the modal markup -->
<div id="modal1" class="modal">
<div class="modal-content">
<h4>Delete this record?</h4>
<p>This action cannot be undone.</p>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect btn-flat">Cancel</a>
<a href="#!" class="waves-effect btn red">Delete</a>
</div>
</div>
<!-- Step 3: initialization (before </body>) -->
<script>
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelectorAll('.modal');
M.Modal.init(elems);
});
</script>Two class names do most of the work. modal-trigger tells Materialize the link opens a modal, and modal-close makes any element inside the dialog dismiss it. If you prefer, M.AutoInit() initializes every component on the page — fine for a demo, but explicit init is safer in production because you control the options each component receives.
Modal options that matter
| Option | Default | What it does |
|---|---|---|
| dismissible | true | Whether clicking the overlay closes the modal |
| opacity | 0.5 | Darkness of the backdrop behind the dialog |
| inDuration | 250 | Open animation, in milliseconds |
| outDuration | 250 | Close animation, in milliseconds |
| preventScrolling | true | Locks body scroll while the modal is open |
| startingTop | ‘4%’ | Vertical start position of the entry animation |
| onOpenEnd | null | Callback fired after the open animation finishes |
| onCloseEnd | null | Callback fired after the close animation finishes |
M.Modal.init(document.querySelectorAll('.modal'), {
dismissible: true,
opacity: 0.6,
inDuration: 300,
outDuration: 200,
onOpenEnd: function () { console.log('modal opened'); }
});Setting dismissible: false is the option for confirmations the user must answer — it forces a choice between the buttons instead of an accidental overlay click. Keep preventScrolling: true on long dialogs; disabling it lets the page behind scroll on phones while the modal is open.
Opening and closing a modal from JavaScript
Trigger links cover the common case, but sometimes you need to open a dialog from code — after a form submits, on a timer, or from a button built dynamically. Grab the instance once and call its methods:
// initialize once, keep the instance
var instance = M.Modal.getInstance(document.querySelector('#modal1'));
instance.open(); // show it
instance.close(); // hide it
instance.destroy(); // remove event listeners (single-page apps)The full pattern for auto-opening on page load — including why an immediate open() call races the DOM — is documented in the open a modal on page load guide. If your dialog body holds a styled button, the same classes used in the button guide apply inside the modal footer.
When the modal does not open: check these four things
- href / id mismatch. The trigger
href="#modal1"must equal the modal’sid="modal1"exactly. A capital letter or trailing space is enough to kill it. - No initialization. Markup alone does nothing on 1.0.0.
M.Modal.init()orM.AutoInit()is mandatory. - Modal stuck in a transformed parent. A parent with
transform,filteroroverflow: hiddenbecomes the containing block for the fixed overlay, clipping it or pushing it off screen. Move the modal to a direct child ofbody. - Old 0.97.x tutorial code. The jQuery era used
$('.modal').modal(). That API was removed in 1.0.0 and silently fails today.
If you are still deciding which framework to build on, the Materialize vs Bootstrap vs Tailwind comparison puts the component trade-offs side by side. The Materialize CSS tutorial that opens this series covers setup and version handling in depth.
Want a second pair of eyes on a dialog-heavy interface? Ampersand Academy works through real components like modals one-to-one, using your own project as the curriculum.
Frequently asked questions
Why is my Materialize modal not opening?
The usual causes are a trigger href that does not match the modal id, a missing M.Modal.init() call, or the modal sitting inside a parent with a CSS transform that clips the fixed overlay.
How do I stop a Materialize modal from closing on background click?
Initialize it with the dismissible option set to false: M.Modal.init(elems, { dismissible: false }). The overlay then ignores clicks and the user must use your buttons.
How do I open a Materialize modal with JavaScript?
Get the instance with M.Modal.getInstance(elem) and call the open() method. You can also call close() and destroy() from the same instance object.
Does Materialize 1.0.0 still need jQuery for modals?
No. Materialize 1.0.0 dropped jQuery and uses M.Modal.init() plus M.AutoInit(). The old jQuery-based initialization code simply errors and no longer works.
How do I add a close button inside a Materialize modal?
Add the modal-close class to any element inside the dialog, usually a flat button in the modal-footer. Materialize wires the close behavior automatically at init time.
