Toasts

A toast is the small, dismissible notification that slides in near a corner of the screen — "Trail saved to favorites," "Message sent," that kind of thing. Every component so far in this section opened purely from data-bs-* attributes. Toasts are the first one that genuinely needs a line of real JavaScript, and it's worth understanding exactly why.

Anatomy of a Toast

ClassRole
.toastThe outer element — hidden by default, role="alert" for screen readers.
.toast-headerSmall icon/title bar with a timestamp and close button.
.toast-bodyThe actual message text.
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
  <div class="toast-header">
    <strong class="me-auto">TrailMap</strong>
    <small>just now</small>
    <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
  </div>
  <div class="toast-body">
    Trail saved to favorites.
  </div>
</div>

Why Toasts Need a Real JavaScript Call

Modals, offcanvas panels, collapses, and accordions are all things a user explicitly opens by clicking a visible trigger — data-bs-toggle on that trigger is enough, because there's always a click event to listen for. A toast is different: it's meant to appear in response to something the page's own code just did — a favorite was saved, a form submitted successfully, a background sync finished. There's no natural "trigger element" for Bootstrap's data-attribute system to watch, so toasts are hidden by display: none by default and need to be shown programmatically:

const toastEl = document.querySelector('#favoriteToast');
const toast = new bootstrap.Toast(toastEl);
toast.show();
Note: this is a deliberate exception, not an oversight. Every Bootstrap 5 JS component exposes this same constructor pattern (new bootstrap.Modal(el), new bootstrap.Toast(el), etc.) — data attributes are just a convenience layer built on top for the click-to-open case. Toasts are one of the few where you're expected to reach for the constructor directly.

Positioning with .toast-container

A bare .toast sits inline wherever it's placed in the document. Wrap it in .toast-container with position utilities to pin it to a screen corner and stack multiple toasts cleanly:

<div class="toast-container position-fixed bottom-0 end-0 p-3">
  <!-- .toast elements go here -->
</div>

Autohide and Delay

Toasts autohide after 5 seconds by default. Both behaviors are controlled with data attributes on the .toast element itself — these are read when the bootstrap.Toast instance is created, even though showing it still requires the JS call above:

AttributeEffect
data-bs-autohide="false"Keeps the toast visible until the user dismisses it manually.
data-bs-delay="3000"Sets the autohide delay in milliseconds (default is 5000).

Worked Example: "Trail Saved to Favorites"

A heart-icon button on a TrailMap trail card saves it to the signed-in user's favorites and confirms it with a toast in the bottom-right corner:

<!-- Trigger button, elsewhere on the page -->
<button type="button" class="btn btn-outline-danger" id="saveFavoriteBtn">
  <i class="bi bi-heart"></i> Save Trail
</button>

<!-- Toast, positioned bottom-right -->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
  <div id="favoriteToast" class="toast" role="alert"
    data-bs-delay="3000" aria-live="assertive" aria-atomic="true">
    <div class="toast-header">
      <i class="bi bi-heart-fill text-danger me-2"></i>
      <strong class="me-auto">TrailMap</strong>
      <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
    </div>
    <div class="toast-body">
      Trail saved to favorites.
    </div>
  </div>
</div>

<!-- JS: fire the toast when the button is clicked -->
<script>
  const favoriteToastEl = document.querySelector('#favoriteToast');
  const favoriteToast = new bootstrap.Toast(favoriteToastEl);

  document.querySelector('#saveFavoriteBtn').addEventListener('click', function () {
    // ...save to favorites via your own API call...
    favoriteToast.show();
  });
</script>

The toast's own markup only handles its appearance and dismiss button — TrailMap's own click handler decides when to call .show(), which is exactly the control a "did something just succeed?" notification needs.

Next: Head to Tooltips & Popovers — two more components that require JS initialization, though for a different reason than toasts.