Bootstrap Modal: Sizes, Centering, Forms and JavaScript
A Bootstrap modal is a dialog box that opens on top of the page and dims everything behind it: a confirmation (“Delete this project?”), a sign-up form, an image preview, or a video. The modal plugin handles the backdrop, keyboard focus, the Escape key, and scroll locking, so you only write the markup. This guide covers that markup, opening and closing a modal, sizes (including the new extra-extra-large size in Bootstrap 5.3.9), centered, scrolling and fullscreen modals, forms inside modals, the JavaScript API, and the problems people hit most often.
A working modal
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#delete-modal">
Delete project
</button>
<div class="modal fade" id="delete-modal" tabindex="-1" aria-labelledby="delete-modal-title" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="delete-modal-title">Delete this project?</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
The project and its 12 pages will be removed. This can't be undone.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
Load Bootstrap’s JavaScript (bootstrap.bundle.min.js or bootstrap.min.js, since modals don’t need Popper). Unlike tooltips, modals work from data attributes alone, with no initialization code.
| Part | Job |
|---|---|
data-bs-toggle="modal" and data-bs-target |
Make any button open the modal with that id |
.modal |
The full-screen layer that holds the dialog; .fade adds the animation |
.modal-dialog |
Sets the width and position |
.modal-content |
The white box with the border and shadow |
.modal-header, .modal-body, .modal-footer |
The three optional sections |
data-bs-dismiss="modal" |
Makes a button close the modal |
tabindex="-1" lets the modal receive focus, and aria-labelledby points screen readers to the title. Bootstrap moves focus into the modal when it opens and back to the button when it closes.
Sizes
Add a size class to .modal-dialog:
| Class | Max width | Applies from |
|---|---|---|
modal-sm |
300px | 576px screens |
| (none) | 500px | 576px screens |
modal-lg |
800px | 992px screens |
modal-xl |
1140px | 1200px screens |
modal-xxl |
1320px | 1400px screens (new in 5.3.9) |
<div class="modal-dialog modal-lg">...</div>
On smaller screens every modal is full width minus a small margin. modal-xxl arrived in Bootstrap 5.3.9; on older 5.3 versions, use modal-xl or set a custom width with the --bs-modal-width variable:
.modal-wide { --bs-modal-width: 960px; }
Vertically centered
<div class="modal-dialog modal-dialog-centered">...</div>
By default a modal sits near the top of the screen. modal-dialog-centered puts it in the middle, which suits short confirmations.
Long content that scrolls
When the content is longer than the screen, the whole modal scrolls with the page. To keep the header and footer in place and scroll only the body, add modal-dialog-scrollable:
<div class="modal-dialog modal-dialog-scrollable">...</div>
This is the better choice for terms of service or long forms, because the action buttons stay visible.
Fullscreen modals
<div class="modal-dialog modal-fullscreen">...</div>
<div class="modal-dialog modal-fullscreen-md-down">...</div>
modal-fullscreen always covers the screen. The responsive versions, from modal-fullscreen-sm-down to modal-fullscreen-xxl-down, go fullscreen only below that breakpoint, which works well for forms on phones. The breakpoint values are in the breakpoints guide.
A modal that doesn’t close on outside click
By default, clicking the backdrop or pressing Escape closes the modal. For a step people must finish or cancel explicitly, turn both off:
<div class="modal fade" id="terms-modal" data-bs-backdrop="static" data-bs-keyboard="false"
tabindex="-1" aria-labelledby="terms-title" aria-hidden="true">
With a static backdrop, a click outside makes the modal bounce briefly instead of closing. Always keep a visible button that closes it.
A form inside a modal
Wrap the content in a <form> so Enter submits it and the submit button sits in the footer:
<div class="modal fade" id="invite-modal" tabindex="-1" aria-labelledby="invite-title" aria-hidden="true">
<div class="modal-dialog">
<form class="modal-content" id="invite-form">
<div class="modal-header">
<h1 class="modal-title fs-5" id="invite-title">Invite a teammate</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<label for="invite-email" class="form-label">Email</label>
<input type="email" class="form-control" id="invite-email" required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Send invite</button>
</div>
</form>
</div>
</div>
The autofocus attribute doesn’t work inside modals, because the modal isn’t visible when the page loads. Focus the field once the modal has opened:
const inviteModal = document.querySelector('#invite-modal')
inviteModal.addEventListener('shown.bs.modal', () => {
document.querySelector('#invite-email').focus()
})
Form layout, validation, and input groups are covered in the Bootstrap forms guide.
Open and close a modal with JavaScript
const modal = bootstrap.Modal.getOrCreateInstance('#delete-modal')
modal.show()
modal.hide()
modal.toggle()
To close the modal after a form is sent:
document.querySelector('#invite-form').addEventListener('submit', async (event) => {
event.preventDefault()
await fetch('/api/invite', { method: 'POST', body: new FormData(event.target) })
bootstrap.Modal.getInstance('#invite-modal').hide()
})
To open a modal when the page loads, call show() once the page is ready, for example bootstrap.Modal.getOrCreateInstance('#welcome-modal').show() at the end of the page. Use this sparingly: modals that appear without a click annoy visitors and can hurt mobile search rankings.
One modal, different content
For a list of items that each open the same modal with different details, read the clicked button from event.relatedTarget:
<button class="btn btn-link" data-bs-toggle="modal" data-bs-target="#user-modal" data-user="Ana Lopez">Ana</button>
<button class="btn btn-link" data-bs-toggle="modal" data-bs-target="#user-modal" data-user="Ben Carter">Ben</button>
document.querySelector('#user-modal').addEventListener('show.bs.modal', (event) => {
const name = event.relatedTarget.getAttribute('data-user')
event.target.querySelector('.modal-title').textContent = name
})
Events
| Event | Fires |
|---|---|
show.bs.modal |
When opening starts; call event.preventDefault() to cancel |
shown.bs.modal |
When the modal is fully visible |
hide.bs.modal |
When closing starts; can also be cancelled |
hidden.bs.modal |
When the modal is fully hidden |
hidePrevented.bs.modal |
When a static modal blocks a close attempt |
Style modals
Modals use CSS variables:
.modal {
--bs-modal-border-radius: 1rem;
--bs-modal-padding: 1.5rem;
--bs-modal-header-border-color: transparent;
--bs-modal-footer-border-color: transparent;
}
.modal-backdrop { --bs-backdrop-opacity: .7; }
In Sass, $modal-inner-padding, $modal-content-border-radius, $modal-backdrop-opacity (0.5), and the size variables $modal-sm to $modal-xxl set the defaults. Modals follow dark mode on their own.
Common modal problems
The modal opens behind the gray backdrop. The modal is inside an element with position: fixed, transform, or a z-index, such as a fixed navbar. Move the modal markup to just before </body>.
Nothing happens on click. Bootstrap’s JavaScript isn’t loaded, or the data-bs-target doesn’t match the modal’s id. Check the browser console for errors.
The modal opens and immediately closes. Bootstrap’s JavaScript is loaded twice, for example from a CDN and from your bundle. Keep one copy.
The old attributes don’t work. Bootstrap 5 uses data-bs-toggle, data-bs-target, and data-bs-dismiss. The Bootstrap 4 names without bs- do nothing.
The page jumps when the modal opens. Bootstrap hides the page scrollbar and adds padding to make up for it. Fixed elements need the same padding; Bootstrap adjusts .fixed-top, .fixed-bottom, and .sticky-top automatically, and any element with the is-fixed class, so add is-fixed to your other fixed elements.
FAQ
How do I open a Bootstrap modal with a button?
Add data-bs-toggle="modal" and data-bs-target="#your-modal-id" to the button, and make sure Bootstrap’s JavaScript is loaded. No initialization code is needed. To open it from JavaScript, call bootstrap.Modal.getOrCreateInstance('#your-modal-id').show().
How do I center a Bootstrap modal vertically?
Add the modal-dialog-centered class to the .modal-dialog element. Combine it with modal-dialog-scrollable if the content can be longer than the screen.
How do I change the width of a Bootstrap modal?
Add modal-sm (300px), modal-lg (800px), modal-xl (1140px), or modal-xxl (1320px, new in Bootstrap 5.3.9) to .modal-dialog. For any other width, set the --bs-modal-width CSS variable on the modal.
How do I stop a Bootstrap modal from closing when clicking outside?
Add data-bs-backdrop="static" to the .modal element, and data-bs-keyboard="false" to also ignore the Escape key. Keep a close or cancel button so people can still leave.
Why does my Bootstrap modal appear behind the backdrop?
A parent element has position: fixed, a transform, or a z-index that traps the modal below the backdrop. Move the modal’s markup out of that element, ideally to just before the closing </body> tag.
Try it in the builder
Make these changes visually with a live preview, then export clean Bootstrap Sass or CSS.
Open the Builder