Lesson 10 of 12

Bootstrap Modals and Alerts

Alerts and modals both get the user’s attention, but in very different ways. An alert is a coloured message box inside the page, such as “Your profile was saved”. A modal is a dialog box that opens on top of the page and blocks everything behind it until it is closed. Knowing when to use alerts or Bootstrap modals is as important as knowing the classes.

Alerts

An alert is a <div> with alert and a colour class such as alert-success, alert-danger, alert-warning or alert-info. Inside, use alert-heading for a title and alert-link on links so they get a matching, bolder colour.

Add role="alert" when the message appears as a result of something the user did, such as submitting a form. Screen readers then announce it immediately. For a message that is simply part of the page when it loads, you can leave the role out, because an interrupting announcement would be unexpected.

To let users close an alert, add alert-dismissible fade show and a close button with btn-close and data-bs-dismiss="alert". The close button needs an aria-label, because it shows only an “×” icon. Dismissing needs the Bootstrap JavaScript bundle.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alerts, including a dismissible one</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container py-4">
  <div class="alert alert-success" role="alert">
    <h2 class="alert-heading h5">Booking confirmed</h2>
    <p class="mb-0">Your homestay in Kodaikanal is booked for 12–14 December. <a href="#" class="alert-link">View booking</a>.</p>
  </div>

  <div class="alert alert-warning alert-dismissible fade show" role="alert">
    <strong>Heavy rain expected.</strong> Some hill roads may close this weekend.
    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  </div>

  <div class="alert alert-info mb-0">Tip: you can change your travel dates up to 48 hours before check-in.</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Creating alerts with JavaScript

Most real alerts are created in response to an action: after saving, after an error, after copying a link. A small function can build the alert markup and add it to a placeholder element on the page. Because the alert includes data-bs-dismiss="alert", Bootstrap makes the close button work automatically.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Showing an alert after a click</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container py-4">
  <div id="alertArea"></div>
  <button type="button" class="btn btn-primary" id="saveBtn">Save draft</button>
  <button type="button" class="btn btn-outline-danger" id="failBtn">Simulate an error</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
const alertArea = document.getElementById('alertArea');

function showAlert(message, type) {
  const box = document.createElement('div');
  box.className = 'alert alert-' + type + ' alert-dismissible fade show';
  box.setAttribute('role', 'alert');
  box.textContent = message;

  const close = document.createElement('button');
  close.type = 'button';
  close.className = 'btn-close';
  close.setAttribute('data-bs-dismiss', 'alert');
  close.setAttribute('aria-label', 'Close');
  box.append(close);

  alertArea.append(box);
}

document.getElementById('saveBtn').addEventListener('click', () => {
  showAlert('Draft saved at ' + new Date().toLocaleTimeString('en-IN'), 'success');
});
document.getElementById('failBtn').addEventListener('click', () => {
  showAlert('Could not save. Please check your internet connection.', 'danger');
});

showAlert('Click a button to add more alerts. Use the X to close them.', 'info');
</script>
</body>
</html>

The function uses textContent rather than innerHTML for the message. If a message ever contains user input, this stops it being run as HTML. You can review this technique in JavaScript DOM Manipulation.

Alert or modal?

Before reaching for either component, ask how urgent the message is and whether the user must respond:

  • Alert: information the user should notice but can keep working around, such as a saved confirmation, a form error summary or a service notice.
  • Modal: a decision the user must make before continuing, such as confirming a deletion or choosing between two options.
  • Neither: for help text or optional extras, plain content on the page is calmer and easier to find again later.

Structure of Bootstrap modals

A modal has four nested layers: modal (the full-screen wrapper and backdrop), modal-dialog (positions and sizes the box), modal-content (the white box) and inside it modal-header, modal-body and modal-footer. The modal markup usually sits near the end of <body>, hidden until it is opened.

A trigger button opens it with data-bs-toggle="modal" and data-bs-target="#idOfModal". Any element inside with data-bs-dismiss="modal" closes it. The modal also closes when you press Esc or click the dark backdrop.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A modal dialog</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container py-4">
  <p>Delete the saved address "Anna Nagar, Chennai"?</p>
  <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">Delete address</button>
  <p class="mt-3" id="result" role="status">No action yet.</p>
</div>

<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title fs-5" id="deleteTitle">Delete this address?</h2>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <p class="mb-0">"Anna Nagar, Chennai" will be removed from your saved addresses. This cannot be undone.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Keep it</button>
        <button type="button" class="btn btn-danger" id="confirmDelete">Delete</button>
      </div>
    </div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
const modalEl = document.getElementById('deleteModal');
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
const result = document.getElementById('result');

document.getElementById('confirmDelete').addEventListener('click', () => {
  result.textContent = 'Address deleted.';
  modal.hide();
});

modalEl.addEventListener('hidden.bs.modal', () => {
  if (result.textContent === 'No action yet.') {
    result.textContent = 'You closed the dialog. Nothing was deleted.';
  }
});
</script>
</body>
</html>

Bootstrap modals take care of the hard accessibility work: focus moves into the modal when it opens, stays inside it when you press Tab, and returns to the trigger button when it closes. Your part is to give the modal aria-labelledby pointing to its title, give the close button an aria-label, and keep tabindex="-1" on the wrapper.

Options and the JavaScript API

FeatureHow
Sizesmodal-sm, modal-lg, modal-xl on modal-dialog
Full screenmodal-fullscreen, or modal-fullscreen-md-down for phones only
Centredmodal-dialog-centered
Long contentmodal-dialog-scrollable scrolls the body, keeping header and footer visible
Must choose an optiondata-bs-backdrop="static" and data-bs-keyboard="false" on .modal
Open from codebootstrap.Modal.getOrCreateInstance(el).show(), also .hide() and .toggle()
React to eventsshow.bs.modal, shown.bs.modal, hide.bs.modal, hidden.bs.modal

Watch out: Use Bootstrap modals sparingly. They interrupt people and hide the page behind them. Good uses are confirming a destructive action or a short focused task. Do not use a modal for long forms, for information people need to compare with the page, or as a pop-up that appears on page load. Also avoid opening one modal on top of another; Bootstrap does not support stacked modals.

Tip: Bootstrap also has toasts: small, temporary notifications that appear in a corner and fade away, created with bootstrap.Toast. Use them for low-priority confirmations such as “Link copied”. For anything the user must read or act on, an alert in the page is more reliable.

Further reading

Key takeaways

  • Alerts use alert plus a colour; add role="alert" for messages that appear after a user action.
  • Dismissible alerts need alert-dismissible fade show, a btn-close with data-bs-dismiss="alert", and the JS bundle.
  • A modal is modal > modal-dialog > modal-content with header, body and footer.
  • Open modals with data-bs-toggle="modal" and data-bs-target, or with bootstrap.Modal.getOrCreateInstance(el).show().
  • Label modals with aria-labelledby, and use them only for short, focused tasks.

Quick quiz

Check what you learned – pick an answer.

1. Which attribute on a button closes the modal it is inside?

2. You want a modal that does not close when the backdrop is clicked. What do you add?

3. Which event fires after a modal has finished closing?

Practice exercise

Make a "Subscribe to newsletter" button that opens a centred modal containing an email field and a Subscribe button. When Subscribe is clicked, close the modal and show a dismissible green alert on the page that thanks the user (use the typed email in the message, or "you" if it is empty).

Show a hint

Use bootstrap.Modal.getOrCreateInstance() to hide the modal, then build the alert with createElement and textContent.

Show the solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Newsletter modal</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container py-4">
  <div id="msgArea"></div>
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newsModal">Subscribe to newsletter</button>
</div>

<div class="modal fade" id="newsModal" tabindex="-1" aria-labelledby="newsTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-header">
        <h2 class="modal-title fs-5" id="newsTitle">Weekly design tips</h2>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <label for="newsEmail" class="form-label">Email address</label>
        <input type="email" class="form-control" id="newsEmail">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary" id="subscribeBtn">Subscribe</button>
      </div>
    </div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
const newsModal = bootstrap.Modal.getOrCreateInstance('#newsModal');

document.getElementById('subscribeBtn').addEventListener('click', () => {
  const email = document.getElementById('newsEmail').value.trim() || 'you';
  newsModal.hide();

  const box = document.createElement('div');
  box.className = 'alert alert-success alert-dismissible fade show';
  box.setAttribute('role', 'alert');
  box.textContent = 'Thank you! We will send the tips to ' + email + '.';
  const close = document.createElement('button');
  close.type = 'button';
  close.className = 'btn-close';
  close.setAttribute('data-bs-dismiss', 'alert');
  close.setAttribute('aria-label', 'Close');
  box.append(close);
  document.getElementById('msgArea').append(box);
});
</script>
</body>
</html>

Progress is saved in this browser only.