Overlays: tooltips, popovers and toasts

Configure and trigger tooltips and popovers correctly, build toasts that stack and auto-dismiss, and know when an overlay is the wrong tool.

Tooltips and popovers

Tooltips and popovers are opt-in: nothing is initialised from data attributes alone. They also do not appear on touch devices unless you ask for a different trigger, which is the most common complaint after shipping.

import { Tooltip, Popover } from 'bootstrap';

// initialise every opt-in overlay on the page
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((el) => {
  Tooltip.getOrCreateInstance(el, { trigger: 'hover focus' });
});

// popovers need their own pass and usually richer options
document.querySelectorAll('[data-bs-toggle="popover"]').forEach((el) => {
  Popover.getOrCreateInstance(el, {
    trigger: 'click',
    html: true,
    sanitize: true,                       // keep this on for user-supplied content
    customClass: 'shadow-sm',
    delay: { show: 150, hide: 100 }
  });
});
<button type="button" class="btn btn-link p-0"
        data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Copies to clipboard"
        aria-label="Copy to clipboard">
  Copy
</button>

<button type="button" class="btn btn-outline-secondary"
        data-bs-toggle="popover" data-bs-placement="right"
        data-bs-title="Billing period"
        data-bs-content="Yearly plans are invoiced once and save two months."
        aria-label="Explain billing period">
  What's this?
</button>
  • Tooltips are never a place for essential information: they are unreachable on touch, invisible to keyboard users who never hover, and hidden from screen readers that do not read the title attribute.
  • Titles in the DOM come from title or data-bs-title. Bootstrap removes title to stop the native tooltip appearing, so data-bs-title is the safer authoring choice.
  • placement: 'auto' lets Popper flip the overlay when it would overflow the viewport; without it, a tooltip near the top of the page gets cut off.
  • Because tooltips and popovers render into the document body by default, overflow: hidden on the trigger's parent does not clip them — a frequent cause of confusion.

Toasts and stacking

<div class="toast-container position-fixed bottom-0 end-0 p-3" id="toasts"
     aria-live="polite" aria-atomic="true"></div>
import { Toast } from 'bootstrap';

const container = document.getElementById('toasts');

function notify(message, { variant = 'success', delay = 5000 } = {}) {
  // build, do not query: templates keep the markup in one place
  const el = document.createElement('div');
  el.className = `toast align-items-center text-bg-${variant} border-0`;
  el.setAttribute('role', variant === 'danger' ? 'alert' : 'status');
  el.setAttribute('aria-live', variant === 'danger' ? 'assertive' : 'polite');
  el.setAttribute('aria-atomic', 'true');
  el.innerHTML = `
    <div class="d-flex">
      <div class="toast-body"></div>
      <button type="button" class="btn-close btn-close-white me-2 m-auto"
              data-bs-dismiss="toast" aria-label="Close"></button>
    </div>`;
  el.querySelector('.toast-body').textContent = message;   // textContent: never inject HTML here

  container.append(el);
  const toast = new Toast(el, { delay, autohide: delay > 0 });
  // dispose the element when it hides so repeated notifications do not leak nodes
  el.addEventListener('hidden.bs.toast', () => { toast.dispose(); el.remove(); }, { once: true });
  toast.show();
}

notify('Invoice #1042 saved.');
notify('Could not reach the payment provider.', { variant: 'danger', delay: 0 });
⚠️
A container with aria-live must exist in the DOM before the message is inserted, or screen readers announce nothing. Put the empty container in the server-rendered HTML rather than creating it in JavaScript on demand.

Choosing the right overlay

SituationRight toolWhy
Short label for an unfamiliar iconVisible text or a tooltipTooltips hide the answer behind hover
Result of an action the user just tookToastNon-blocking, appears near the work
Confirmation the user must answermodalA toast can be missed and dismisses itself
Extra detail behind a wordInline disclosure or popoverKeyboard reachable and copyable
Errors that block progressInline form feedbackThe message belongs next to the field
Destructive irreversible actionModal with typed confirmationForces a deliberate decision
  • An overlay never receives focus automatically, so anything interactive inside one has to be reachable another way.
  • Toasts stack in DOM order; a fixed container in a corner is predictable. Toasts anchored to the cursor look clever and are hard to click.
  • Auto-hiding is a courtesy, not a guarantee: keep the message available elsewhere for anything a user may need to read after it disappears.

FAQ

Why does my tooltip not work on a phone?
The default trigger is hover focus, neither of which exists on touch. Set trigger: 'click' or an explicit combination such as 'hover focus click', and remember that a touch user now needs a second tap to dismiss it.
Do I need to dispose toasts manually?
Yes, if you create them dynamically. The instance holds listeners and Popper state; removing the node without dispose() leaks it. Listen for hidden.bs.toast and clean up there.

Navigation and disclosure patterns The JavaScript API, plugins and events

Last refreshed 2026-09-18.