Toasts, Spinners and Placeholders

Intermediate
13 min

Toasts, Spinners and Placeholders

Feedback components tell users what the application is doing. Toasts are lightweight notifications that appear briefly in a corner, spinners show that something is loading, and placeholders draw skeleton screens while content arrives. This lesson covers all three, including stacking and positioning toasts, sizing and coloring spinners, and animating placeholders.

Toasts

A toast is hidden until its JavaScript instance calls show(). Toasts require the JavaScript plugin and are placed inside a .toast-container so multiple toasts stack:

html
<div class="toast-container position-fixed top-0 end-0 p-3"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" id="errorToast"> <div class="toast-header text-bg-danger"> <strong class="me-auto">Upload failed</strong> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button> </div> <div class="toast-body">The file exceeds the 5 MB limit.</div> </div> </div>

Position the container with the position utilities (top-0 end-0, bottom-0 start-50 translate-middle-x, and so on). Use role="alert" with aria-live="assertive" for errors and role="status" with aria-live="polite" for informational messages.

Showing Toasts

javascript
import { Toast } from "bootstrap"; const toast = Toast.getOrCreateInstance(document.getElementById("errorToast"), { autohide: true, delay: 5000, animation: true }); toast.show();

| Option | Default | Meaning | |---|---|---| | animation | true | Fade in and out | | autohide | true | Hide automatically | | delay | 5000 | Milliseconds before auto-hiding |

Set autohide: false (or data-bs-autohide="false") for messages that need a user action. Events are show, shown, hide and hidden with the .bs.toast suffix.

Creating Toasts Dynamically

Most apps generate toasts on the fly rather than keeping them in the HTML:

javascript
function notify(message, variant = "primary") { const container = document.querySelector(".toast-container"); const el = document.createElement("div"); el.className = `toast align-items-center text-bg-${variant} border-0`; el.setAttribute("role", "status"); el.setAttribute("aria-live", "polite"); el.innerHTML = ` <div class="d-flex"> <div class="toast-body">${message}</div> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button> </div>`; container.appendChild(el); el.addEventListener("hidden.bs.toast", () => el.remove()); Toast.getOrCreateInstance(el).show(); } notify("Settings saved", "success");

This is the compact, header-less toast style with text-bg-* color. Removing the element after hidden.bs.toast keeps the DOM clean.

Spinners

Spinners are CSS-only. Two styles exist:

html
<div class="spinner-border text-primary" role="status"> <span class="visually-hidden">Loading...</span> </div> <div class="spinner-grow text-secondary" role="status"> <span class="visually-hidden">Loading...</span> </div>
  • Color with text-{color} utilities.
  • Size with spinner-border-sm / spinner-grow-sm, or set style="width: 3rem; height: 3rem" for larger sizes.
  • Always include visually hidden text so screen readers announce the state.

Inside a button, place a small spinner before the label and hide the spinner until needed, as in the sample at the top. If the button text is removed while loading, keep a .visually-hidden label. Center a standalone spinner with d-flex justify-content-center on the parent or d-block mx-auto on the spinner itself.

Placeholders

Placeholders render grey blocks the shape of upcoming content. They are also CSS-only:

html
<div class="card" aria-hidden="true" style="max-width: 320px"> <div class="card-body"> <h5 class="card-title placeholder-glow"> <span class="placeholder col-6"></span> </h5> <p class="card-text placeholder-glow"> <span class="placeholder col-7"></span> <span class="placeholder col-4"></span> <span class="placeholder col-6"></span> </p> <a class="btn btn-primary disabled placeholder col-6" aria-disabled="true"></a> </div> </div>
  • .placeholder draws the block; give it a width with col-* or w-*.
  • .placeholder-glow on the parent pulses opacity; .placeholder-wave sweeps a highlight across.
  • placeholder-xs, -sm and -lg change the height; bg-* changes the color.
  • Mark the whole skeleton aria-hidden="true" so screen readers skip it, and replace it with real content once loaded.

A typical flow: render the skeleton, fetch data, then swap innerHTML. Because placeholders are just spans, the swap needs no plugin cleanup.

Choosing the Right Indicator

| Situation | Component | |---|---| | Confirming a completed action | Toast | | Short wait on a button or small area | Spinner | | Content that has a known shape (cards, lists) | Placeholder | | Blocking error that needs acknowledgement | Modal or alert, not a toast |

Common Mistakes

  • Calling show() before the JavaScript is loaded. The toast stays hidden; check for bootstrap.Toast in the console.
  • Toasts outside a .toast-container. They overlap instead of stacking.
  • Spinner without a label. Screen readers get no indication that anything is happening.
  • Leaving skeletons in the accessibility tree. Add aria-hidden="true".
Quick Quiz
Question 1 of 3

What does the `delay` option of a toast control?

Key Takeaways

  • Toasts live inside a positioned .toast-container and need Toast.getOrCreateInstance(el).show(); autohide and delay control dismissal.
  • Generate toasts dynamically and remove them on hidden.bs.toast; use role/aria-live appropriate to the message.
  • .spinner-border and .spinner-grow are CSS-only; color with text-*, size with -sm or inline width/height, and include hidden label text.
  • .placeholder spans with placeholder-glow or placeholder-wave build skeleton screens; mark them aria-hidden.
  • Pick toast, spinner or placeholder based on whether you are confirming, waiting, or loading shaped content.

Next lesson: Scrollspy - highlight navigation links automatically as the user scrolls through page sections.

Toasts, Spinners and Placeholders - Bootstrap | CodeYourCraft | CodeYourCraft