Bootstrap Tooltip: 16 Examples, Options and Popovers

A Bootstrap tooltip stays hidden until **new bootstrap.Tooltip() runs on the element**. Bootstrap 5.3 makes tooltips opt-in for performance reasons, and Popper has to be on the page, which bootstrap.bundle.min.js already includes.

Below are 16 Bootstrap tooltip examples you can copy, followed by a guide to placement, triggers, HTML content, styling, accessibility and the errors that stop tooltips from showing.

By Bogdan Sandu ยท Updated October 29, 2026

Every example runs on Bootstrap 5.3.8 from the jsDelivr CDN. Add these tags to your page, then paste an example's HTML, CSS and JS.

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet" integrity="sha384-nWN3KqaaVcbEsCjLSV+2jMjh62Iny/+mN3srM8TZeM8N6aj7eDl5wwYggjAJ4/sJ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>

Placement, triggers and timing

The four placements, icon-only toolbars, focus-only hints and delay settings. Every demo creates its tooltips in its own script, because Bootstrap tooltips are opt-in and stay silent until new bootstrap.Tooltip() runs.

Four placements around a photo viewer

  • data-bs-placement
  • data-bs-title
  • aria-label
  • fallbackPlacements

Hover a button or tab to it with the keyboard.

Each action button sits on one side of the photo and its tooltip opens away from it with data-bs-placement top, right, bottom or left. On the narrow mobile frame Popper flips a side tooltip through fallbackPlacements when it runs out of room. The buttons are icon-only, so each one also carries an aria-label.

View code
<div class="btp-viewer">
  <button type="button" class="btn btn-light border btp-v-top" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Add to favorites" aria-label="Add to favorites"><i class="bi bi-heart" aria-hidden="true"></i></button>
  <button type="button" class="btn btn-light border btp-v-left" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="Previous photo" aria-label="Previous photo"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
  <figure class="btp-v-photo mb-0">
    <img src="mountain-hiker.webp" alt="Hiker with an orange backpack looking over a misty mountain valley at sunrise" class="img-fluid rounded-3 shadow-sm" width="800" height="600">
  </figure>
  <button type="button" class="btn btn-light border btp-v-right" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title="Next photo" aria-label="Next photo"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
  <button type="button" class="btn btn-light border btp-v-bottom" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Download full size" aria-label="Download full size"><i class="bi bi-download" aria-hidden="true"></i></button>
</div>

Icon-only editor toolbar

  • btn-group
  • btn-toolbar
  • aria-label
  • data-bs-title

Hover a button or tab to it with the keyboard.

A text editor toolbar built from btn-toolbar and btn-group, with a tooltip naming each icon. Bootstrap only copies a title attribute into aria-label, so with data-bs-title every icon-only button needs its own aria-label. The keyboard shortcut is part of the tooltip text as a bonus for mouse users.

View code
<div class="btp-editor bg-body border rounded-3 shadow-sm">
  <div class="btn-toolbar gap-2 p-2 border-bottom bg-body-tertiary rounded-top-3" role="toolbar" aria-label="Formatting">
    <div class="btn-group" role="group" aria-label="Text style">
      <button type="button" class="btn btn-outline-secondary" data-bs-toggle="tooltip" data-bs-title="Bold (Ctrl+B)" aria-label="Bold"><i class="bi bi-type-bold" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-secondary" data-bs-toggle="tooltip" data-bs-title="Italic (Ctrl+I)" aria-label="Italic"><i class="bi bi-type-italic" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-secondary" data-bs-toggle="tooltip" data-bs-title="Insert link (Ctrl+K)" aria-label="Insert link"><i class="bi bi-link-45deg" aria-hidden="true"></i></button>
    </div>
    <div class="btn-group" role="group" aria-label="Blocks">
      <button type="button" class="btn btn-outline-secondary" data-bs-toggle="tooltip" data-bs-title="Bulleted list" aria-label="Bulleted list"><i class="bi bi-list-ul" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-secondary" data-bs-toggle="tooltip" data-bs-title="Quote" aria-label="Quote"><i class="bi bi-quote" aria-hidden="true"></i></button>
    </div>
  </div>
  <p class="p-3 mb-0 text-body-secondary">Draft: Spring menu launch notes for the Harbor Street kitchen team.</p>
</div>

Format hints on input focus

  • trigger: focus
  • form-control
  • form-label
  • aria-describedby

Hover a button or tab to it with the keyboard.

The inputs get a tooltip with trigger set to focus, so the format hint appears while the user types in the field and disappears when they leave it. Bootstrap adds aria-describedby pointing at the tooltip while it is shown, so screen readers hear the hint too. Placement right falls back to top on narrow screens.

View code
<form class="btp-profile bg-body border rounded-3 p-4 shadow-sm" novalidate>
  <h3 class="h5 mb-3">Public profile</h3>
  <div class="mb-3">
    <label for="btp-handle" class="form-label">Username</label>
    <input type="text" class="form-control btp-hint" id="btp-handle" autocomplete="username" data-bs-title="3 to 20 letters, numbers or hyphens" data-bs-placement="right">
  </div>
  <div>
    <label for="btp-phone" class="form-label">Phone</label>
    <input type="tel" class="form-control btp-hint" id="btp-phone" autocomplete="tel" data-bs-title="Start with the country code, for example +44" data-bs-placement="right">
  </div>
</form>

Delay options side by side

  • data-bs-delay
  • delay object
  • btn-outline-*

Hover a button or tab to it with the keyboard.

Three buttons with the same tooltip and different timing: the default 0, a single number that applies to show and hide, and a JSON object that splits them. A short show delay keeps tooltips from flashing while the pointer crosses a dense toolbar.

View code
<div class="btp-delays d-flex flex-wrap justify-content-center gap-2">
  <button type="button" class="btn btn-outline-dark btp-delay" data-bs-title="Shown instantly">No delay</button>
  <button type="button" class="btn btn-outline-dark btp-delay" data-bs-title="300 ms both ways" data-bs-delay="300">delay: 300</button>
  <button type="button" class="btn btn-outline-dark btp-delay btp-delay-split" data-bs-title="Waits 600 ms, hides after 150 ms" data-bs-delay='{"show":600,"hide":150}'>show 600, hide 150</button>
</div>

Disabled, dismissible and changing tooltips

Tooltips that react to state: a wrapper for a disabled button, an Escape key handler Bootstrap does not ship, a Copied! label swapped in with setContent and tooltips created only when text is cut off.

Tooltip on a disabled button

  • d-inline-block
  • tabindex="0"
  • disable()
  • enable()

Hover, focus or click the trigger.

A disabled button gets no pointer or focus events, so the tooltip lives on a span wrapper with tabindex="0", as in the Bootstrap docs. Once both checklist boxes are ticked, the script enables the button, calls disable() on the wrapper tooltip and removes the wrapper from the tab order.

View code
<div class="btp-publish bg-body border rounded-3 p-4 shadow-sm">
  <h3 class="h5 mb-3">Ready to publish?</h3>
  <div class="form-check mb-2">
    <input class="form-check-input btp-check" type="checkbox" id="btp-c1">
    <label class="form-check-label" for="btp-c1">Featured image added</label>
  </div>
  <div class="form-check mb-3">
    <input class="form-check-input btp-check" type="checkbox" id="btp-c2">
    <label class="form-check-label" for="btp-c2">SEO title reviewed</label>
  </div>
  <span class="d-inline-block btp-wrap" id="btp-wrap" tabindex="0" data-bs-title="Finish the checklist to publish">
    <button type="button" class="btn btn-success btp-pub" id="btp-pub" disabled>Publish post</button>
  </span>
</div>

Map controls with Escape to dismiss

  • btn-group-vertical
  • hide()
  • keydown
  • WCAG 1.4.13

Hover, focus or click the trigger.

Bootstrap 5.3.8 tooltips have no Escape handler, which WCAG 1.4.13 asks for. The script keeps the instances in an array and hides them all on Escape, without moving focus or the pointer. The controls are a vertical button group over a map drawn with plain CSS.

View code
<div class="btp-map rounded-3 border shadow-sm">
  <div class="btn-group-vertical btp-map-ctrl shadow-sm" role="group" aria-label="Map controls">
    <button type="button" class="btn btn-light border btp-map-btn" data-bs-title="Zoom in" data-bs-placement="left" aria-label="Zoom in"><i class="bi bi-plus-lg" aria-hidden="true"></i></button>
    <button type="button" class="btn btn-light border btp-map-btn" data-bs-title="Zoom out" data-bs-placement="left" aria-label="Zoom out"><i class="bi bi-dash-lg" aria-hidden="true"></i></button>
    <button type="button" class="btn btn-light border btp-map-btn" data-bs-title="Show my location" data-bs-placement="left" aria-label="Show my location"><i class="bi bi-crosshair" aria-hidden="true"></i></button>
  </div>
  <p class="btp-map-note small mb-0 bg-body rounded-2 px-2 py-1">Press Esc to hide a tooltip.</p>
</div>

Full name for truncated file links

  • text-truncate
  • list-group
  • title option
  • scrollWidth

Hover, focus or click the trigger.

File names are cut with text-truncate, and the script creates a tooltip only for links whose scrollWidth is wider than the visible box, passing the full name through the title option. Names that already fit get no tooltip, so nothing repeats what is on screen.

View code
<div class="btp-files bg-body border rounded-3 shadow-sm">
  <h3 class="h6 px-3 pt-3 mb-2">Recent uploads</h3>
  <div class="list-group list-group-flush rounded-bottom-3">
    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center gap-2 btp-file"><i class="bi bi-file-earmark-pdf text-danger" aria-hidden="true"></i><span class="text-truncate btp-file-name">Q3-brand-guidelines-final-approved-by-legal-v7.pdf</span></a>
    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center gap-2 btp-file"><i class="bi bi-file-earmark-image text-primary" aria-hidden="true"></i><span class="text-truncate btp-file-name">homepage-hero-illustration-dark-mode-export@2x.png</span></a>
    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center gap-2 btp-file"><i class="bi bi-file-earmark-text text-secondary" aria-hidden="true"></i><span class="text-truncate btp-file-name">notes.txt</span></a>
  </div>
</div>

Forms, data and richer content

Field help, chart values, avatar names and keyboard shortcuts. Two demos use html: true with fixed markup written in the page, never with text a user typed.

Info buttons beside form labels

  • btn-link
  • bi-info-circle
  • aria-label
  • form-text

Hover or focus an element to see its tooltip.

Each label gets a small icon button whose tooltip explains the setting. The button is a real button, so it is focusable and announced, and its aria-label says what it is for. Information the user needs to fill the field stays in visible form-text; the tooltip only adds background.

View code
<form class="btp-settings bg-body border rounded-3 p-4 shadow-sm" novalidate>
  <h3 class="h5 mb-3">Project settings</h3>
  <div class="mb-3">
    <div class="d-flex align-items-center gap-1 mb-2">
      <label for="btp-slug" class="form-label mb-0">URL slug</label>
      <button type="button" class="btn btn-link p-0 btp-info" data-bs-title="The slug is the last part of the project address. Changing it breaks old links." aria-label="About the URL slug"><i class="bi bi-info-circle" aria-hidden="true"></i></button>
    </div>
    <input type="text" class="form-control" id="btp-slug" value="northwind-redesign" aria-describedby="btp-slug-help">
    <div id="btp-slug-help" class="form-text">Lowercase letters, numbers and hyphens.</div>
  </div>
  <div>
    <div class="d-flex align-items-center gap-1 mb-2">
      <label for="btp-vis" class="form-label mb-0">Visibility</label>
      <button type="button" class="btn btn-link p-0 btp-info" data-bs-title="Team members can always see the project. Public adds a read-only link for clients." aria-label="About visibility"><i class="bi bi-info-circle" aria-hidden="true"></i></button>
    </div>
    <select class="form-select" id="btp-vis">
      <option>Team only</option>
      <option>Public</option>
    </select>
  </div>
</form>

Bar chart with value tooltips

  • buttons as data points
  • aria-label
  • offset
  • card

Hover or focus an element to see its tooltip.

A small weekly chart where each bar is a button, so the value tooltip works with the keyboard as well as the mouse, and the aria-label reads the same value. The offset option lifts the tooltip a little further off the bar top. Bootstrap has no chart plugin; the bars are plain elements sized with a custom property.

View code
<div class="card btp-chart shadow-sm">
  <div class="card-body">
    <div class="d-flex justify-content-between align-items-baseline mb-3">
      <h3 class="h6 mb-0">Store visits this week</h3>
      <span class="small text-body-secondary">8,912 total</span>
    </div>
    <div class="btp-bars" role="group" aria-label="Visits per day">
      <button type="button" class="btp-bar" style="--btp-h: 52%" data-bs-title="Mon: 1,064 visits" aria-label="Monday, 1,064 visits"></button>
      <button type="button" class="btp-bar" style="--btp-h: 61%" data-bs-title="Tue: 1,251 visits" aria-label="Tuesday, 1,251 visits"></button>
      <button type="button" class="btp-bar" style="--btp-h: 48%" data-bs-title="Wed: 982 visits" aria-label="Wednesday, 982 visits"></button>
      <button type="button" class="btp-bar" style="--btp-h: 70%" data-bs-title="Thu: 1,433 visits" aria-label="Thursday, 1,433 visits"></button>
      <button type="button" class="btp-bar btp-bar-peak" style="--btp-h: 100%" data-bs-title="Fri: 2,047 visits, best day" aria-label="Friday, 2,047 visits, best day"></button>
      <button type="button" class="btp-bar" style="--btp-h: 66%" data-bs-title="Sat: 1,352 visits" aria-label="Saturday, 1,352 visits"></button>
      <button type="button" class="btp-bar" style="--btp-h: 38%" data-bs-title="Sun: 783 visits" aria-label="Sunday, 783 visits"></button>
    </div>
    <div class="btp-days small text-body-secondary" aria-hidden="true"><span>M</span><span>T</span><span>W</span><span>T</span><span>F</span><span>S</span><span>S</span></div>
  </div>
</div>

Avatar stack with names and a list

  • html: true
  • customClass
  • --bs-tooltip-max-width
  • list-unstyled

Hover or focus an element to see its tooltip.

Overlapping initials show each name on hover or focus. The +3 button uses html: true with a short list written in the markup itself (ul and li are on the default allowList), and a custom class raises --bs-tooltip-max-width and left-aligns the text. Never feed html: true with text that comes from users.

View code
<div class="btp-team bg-body border rounded-3 p-4 shadow-sm">
  <h3 class="h6 mb-3">Reviewers on this design</h3>
  <div class="d-flex align-items-center btp-stack">
    <button type="button" class="btp-av" style="--btp-av: #c2410c" data-bs-title="Ama Mensah" aria-label="Ama Mensah">AM</button>
    <button type="button" class="btp-av" style="--btp-av: #0f766e" data-bs-title="Jonas Berg" aria-label="Jonas Berg">JB</button>
    <button type="button" class="btp-av" style="--btp-av: #6d28d9" data-bs-title="Priya Raman" aria-label="Priya Raman">PR</button>
    <button type="button" class="btp-av btp-av-more" data-bs-html="true" data-bs-custom-class="btp-tip-list" data-bs-title="<ul class='list-unstyled mb-0'><li>Leo Duarte</li><li>Mei Tanaka</li><li>Sam Okafor</li></ul>" aria-label="3 more reviewers: Leo Duarte, Mei Tanaka, Sam Okafor">+3</button>
  </div>
</div>

Shortcut keys through a custom allowList

  • html: true
  • allowList
  • kbd
  • sanitizer

Hover or focus an element to see its tooltip.

The kbd element is not on Bootstrap's default allowList, so the sanitizer would strip it. Each instance gets its own allowList, a copy of bootstrap.Tooltip.Default.allowList plus kbd, which leaves every other tooltip on the page with the stricter default. The markup is fixed in the page and sanitize stays on.

View code
<div class="btp-keys d-flex flex-wrap justify-content-center gap-2">
  <button type="button" class="btn btn-dark btp-key" data-bs-html="true" data-bs-title="Search files <kbd>Ctrl</kbd> + <kbd>P</kbd>"><i class="bi bi-search me-2" aria-hidden="true"></i>Search</button>
  <button type="button" class="btn btn-dark btp-key" data-bs-html="true" data-bs-title="Command palette <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd>"><i class="bi bi-terminal me-2" aria-hidden="true"></i>Commands</button>
  <button type="button" class="btn btn-dark btp-key" data-bs-html="true" data-bs-title="Toggle sidebar <kbd>Ctrl</kbd> + <kbd>B</kbd>"><i class="bi bi-layout-sidebar me-2" aria-hidden="true"></i>Sidebar</button>
</div>

Themed tooltips and popovers

The --bs-tooltip-* and --bs-popover-* variables under a custom class, dark color mode, and two popovers for content that needs a title and a body.

Status tooltips themed with CSS variables

  • data-bs-custom-class
  • --bs-tooltip-bg
  • --bs-tooltip-color
  • --bs-tooltip-opacity

Hover, focus or click the trigger.

Each deploy status badge points data-bs-custom-class at a class that sets --bs-tooltip-bg, --bs-tooltip-color, padding and radius, so the arrow picks up the same color automatically. Opacity goes to 1 for solid colors. All other tooltips on the page keep Bootstrap's defaults.

View code
<div class="btp-deploys bg-body border rounded-3 shadow-sm">
  <h3 class="h6 px-3 pt-3 mb-2">Deployments</h3>
  <ul class="list-group list-group-flush rounded-bottom-3">
    <li class="list-group-item d-flex justify-content-between align-items-center gap-2">
      <span class="text-truncate">main <span class="text-body-secondary">#a41f9c</span></span>
      <button type="button" class="badge rounded-pill border-0 btp-status btp-ok" data-bs-custom-class="btp-tip-ok" data-bs-title="Live since 09:42, build took 1 min 12 s">Live</button>
    </li>
    <li class="list-group-item d-flex justify-content-between align-items-center gap-2">
      <span class="text-truncate">feature/cart <span class="text-body-secondary">#7c03be</span></span>
      <button type="button" class="badge rounded-pill border-0 btp-status btp-fail" data-bs-custom-class="btp-tip-fail" data-bs-title="Failed at the test step: 2 checks did not pass">Failed</button>
    </li>
    <li class="list-group-item d-flex justify-content-between align-items-center gap-2">
      <span class="text-truncate">fix/footer <span class="text-body-secondary">#1e88d0</span></span>
      <button type="button" class="badge rounded-pill border-0 btp-status btp-wait" data-bs-custom-class="btp-tip-wait" data-bs-title="Queued behind 1 build, about 2 minutes">Queued</button>
    </li>
  </ul>
</div>

Dark mode media player controls

  • data-bs-theme="dark"
  • --bs-emphasis-color
  • progress
  • aria-label

Hover, focus or click the trigger.

In dark color mode the tooltip flips by itself: its background is --bs-emphasis-color and its text --bs-body-bg, so it turns light on a dark page with no extra CSS. The player is a card with a thin progress bar and icon-only buttons, each with an aria-label.

View code
<div class="card btp-player border-0 shadow">
  <div class="card-body">
    <p class="mb-0 fw-semibold">Night Shift Radio</p>
    <p class="small text-body-secondary mb-3">Episode 48: Late trains and city lights</p>
    <div class="progress mb-3 btp-prog" role="progressbar" aria-label="Playback position" aria-valuenow="38" aria-valuemin="0" aria-valuemax="100">
      <div class="progress-bar" style="width: 38%"></div>
    </div>
    <div class="d-flex justify-content-center gap-2">
      <button type="button" class="btn btn-outline-light btp-ctl" data-bs-title="Back 15 seconds" aria-label="Back 15 seconds"><i class="bi bi-skip-backward" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-light btp-ctl" data-bs-title="Play" aria-label="Play"><i class="bi bi-play-fill" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-light btp-ctl" data-bs-title="Forward 30 seconds" aria-label="Forward 30 seconds"><i class="bi bi-skip-forward" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-light btp-ctl" data-bs-title="Captions" aria-label="Captions"><i class="bi bi-badge-cc" aria-hidden="true"></i></button>
    </div>
  </div>
</div>

Popover with a title and body

  • bootstrap.Popover
  • data-bs-content
  • --bs-popover-*
  • customClass

Hover, focus or click the trigger.

When a hint needs a heading and a sentence or two, use a popover: data-bs-title fills the header and data-bs-content the body. It opens on click by default. A custom class sets --bs-popover-header-bg, --bs-popover-border-color and --bs-popover-max-width to match the plan card.

View code
<div class="card btp-plan shadow-sm">
  <div class="card-body p-4">
    <h3 class="h5 mb-1">Studio plan</h3>
    <p class="display-6 fw-semibold mb-1">$24<span class="fs-6 fw-normal text-body-secondary"> / seat / month</span></p>
    <p class="mb-3">
      <button type="button" class="btn btn-link p-0 btp-pop" data-bs-custom-class="btp-pop-plan" data-bs-title="What counts as a seat?" data-bs-content="A seat is anyone who can edit. Viewers and clients who only comment are free, and you can move a seat to a new person at any time.">What counts as a seat?</button>
    </p>
    <button type="button" class="btn btn-dark w-100 btp-plan-cta">Start 14-day trial</button>
  </div>
</div>

Glossary popover that closes on the next click

  • trigger: focus
  • tabindex="0"
  • role="button"
  • bootstrap.Popover

Hover, focus or click the trigger.

Terms in an article open a short definition and close on the next click anywhere, thanks to trigger focus. Bootstrap's docs build this dismiss-on-next-click popover on an a element with tabindex="0" and role="button" (not a button element) for consistent cross-browser behavior, and that is the markup here.

View code
<article class="btp-article bg-body border rounded-3 p-4 shadow-sm">
  <h3 class="h5">Why the page jumps while it loads</h3>
  <p class="mb-0">If an ad slot or image has no reserved space, the text below it moves when it arrives. That movement is measured as <a tabindex="0" role="button" class="btp-term" data-bs-title="Cumulative Layout Shift" data-bs-content="A Core Web Vitals metric that scores unexpected movement of visible content. Lower is better.">CLS</a>, and you fix most of it by setting <a tabindex="0" role="button" class="btp-term" data-bs-title="Aspect ratio" data-bs-content="The width-to-height ratio of a box. Width and height attributes on images let the browser reserve it early.">width and height</a> on media.</p>
</article>

What Is a Bootstrap Tooltip?

Hover over an element, or tab onto it, and a short text label shows up beside it. That’s the plugin at work. Popper positions the label, and the text comes from the element’s title or data-bs-title attribute.

It’s Bootstrap’s take on the tooltip pattern in UI design, which means a hint and never the main content. If users need the information to finish a task, it belongs in visible text.

When it renders, the plugin builds a div.tooltip wrapper with role="tooltip". Inside that sit a .tooltip-arrow element and a .tooltip-inner element that holds the text.

None of this markup exists up front. The documentation says tooltips are placed right after the trigger element by default, but in the v5.3.8 source the default container is document.body, so that’s where the tooltip is appended unless you set the container option. Before the first show, the DOM has no trace of it.

The plugin ships with the Bootstrap framework, and this article follows the 5.3 line. The current documentation lists v5.3.8, released on 25 August 2025 (Bootstrap blog).

Older syntax still dominates Bootstrap sites. W3Techs data from 28 October 2026 puts Bootstrap 3 on 28.2% of Bootstrap sites, Bootstrap 4 on 21.4% and Bootstrap 5 on 10.7%.

That gap shows in search results, where several tutorials still teach data-toggle and jQuery initialization. Check the version before copying any snippet.

One more thing it isn’t: a container for rich content. A popover takes a title plus a separate content body, while a tooltip carries a single short label.

How to Add a Bootstrap Tooltip

Load Bootstrap’s CSS and the JavaScript bundle, put data-bs-toggle="tooltip" and a title on the element, then create a bootstrap.Tooltip instance for it. Skip that last step and nothing shows.

How you load the files comes first. The Bootstrap 5.3.8 download page lists these routes:

RouteWhat to loadPopper
CDN bundle (jsDelivr)bootstrap.bundle.min.jsIncluded
CDN, separate filespopper.min.js, then bootstrap.min.jsLoad it first
npmnpm install bootstrap@5.3.8Imported as @popperjs/core

After that, work through the steps in order:

  1. Add the Bootstrap CSS link and the bundle script.
  2. Put data-bs-toggle="tooltip" on the element.
  3. Give it text with title or data-bs-title.
  4. Select every trigger with querySelectorAll.
  5. Pass each one to new bootstrap.Tooltip(), after the elements exist in the DOM.
<button type="button" class="btn btn-secondary"
    data-bs-toggle="tooltip" data-bs-placement="top"
    data-bs-title="Tooltip on top">
  Hover me
  </button>

<script>
  const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
    const tooltipList = [...tooltipTriggerList].map(el => new bootstrap.Tooltip(el))
  </script>

For a single element, the constructor also takes a selector, as in new bootstrap.Tooltip('#example'). It uses querySelector, so it only ever finds one element.

Either title or data-bs-title works. With title, the documentation says the attribute is replaced by data-bs-title when the element renders (the v5.3.8 source does something slightly different, covered under methods and events below). A zero-length title never displays a tooltip.

Bootstrap 5 doesn’t need jQuery. Plain JavaScript initialization is the documented route, though Bootstrap does add a jQuery plugin interface when it detects jQuery on the page.

Changing Tooltip Placement, Trigger and Delay

Placement uses data-bs-placement, and the trigger event uses data-bs-trigger. Timing goes through data-bs-delay. All of them have defaults, so none is required.

The Bootstrap 5.3 documentation sets these defaults:

  • Placement is top
  • Trigger is hover focus
  • Offset is [0, 6] (skidding first, then distance)
  • Show and hide delay are both 0 ms
  • Animation is true, which gives a CSS fade

Placement and fallback behavior

Placement takes top, right, bottom or left. There’s also auto, which lets Popper reorient the tooltip to wherever room is left.

When a fixed placement doesn’t fit, Popper flips the tooltip through fallbackPlacements, which defaults to top, right, bottom, left in that order. Directions mirror automatically in right-to-left layouts.

The boundary option sets the overflow constraint and defaults to clippingParents. An element reference overrides it, but only from JavaScript.

const tooltip = new bootstrap.Tooltip('#example', {
  boundary: document.body,
  fallbackPlacements: ['bottom', 'top']
})

Offset can come from a data attribute too, as comma-separated values, for example data-bs-offset="10,20".

Trigger values

Hover fires when the pointer enters the element, so on its own it leaves keyboard users out. Focus fires when the element receives focus, including by tab. Click toggles the tooltip on every click.

Manual hands control to show, hide and toggle calls, and it can’t be combined with another trigger.

To use several triggers, separate them with a space, as the default hover focus does. The docs warn that hover alone produces tooltips keyboard users can’t open, so keep focus unless the same information is available another way.

Delay, animation and container

<button data-bs-toggle="tooltip"
data-bs-delay='{"show":500,"hide":100}'
data-bs-title="Slow to appear">
Delayed tooltip
</button>

A single number applies to show and hide alike, and the object form splits them. Delay is ignored for the manual trigger.

Animation applies a CSS fade and respects the prefers-reduced-motion media query. Set animation: false to remove it entirely.

The container option appends the tooltip to another element. The docs advise container: 'body' for input groups and button groups, where rendering problems can occur, although the v5.3.8 source already defaults to document.body.

How to Set Tooltip Content, HTML and Sanitizer Rules

Text comes from title or data-bs-title. HTML needs data-bs-html="true", and anything the sanitizer doesn’t recognize is stripped before it reaches the page.

HTML content

<button type="button" class="btn btn-secondary"
    data-bs-toggle="tooltip" data-bs-html="true"
    data-bs-title="<em>Tooltip</em> <u>with</u> <b>HTML</b>">
  Tooltip with HTML
  </button>

With html set to false (the default), Bootstrap inserts the title as plain text (the docs say innerText, the v5.3.8 source sets textContent), so tags show up as literal characters. Any HTML markup in a title therefore needs the option turned on.

Prefer plain text whenever the content comes from user input. The docs point to OWASP’s cross-site scripting guidance for exactly that case.

Changing content after initialization

Use setContent instead of a second constructor call.

const tooltip = bootstrap.Tooltip.getInstance('#example')
tooltip.setContent({ '.tooltip-inner': 'another title' })

Keys are selectors inside the tooltip template, and values can be a string, an element, a function or null.

The title option also accepts a function, called with the trigger element as this. The template option swaps the base markup, as long as the outer wrapper keeps the .tooltip class and role="tooltip".

Sanitizer and allowList

Sanitization is on by default and removes every tag and attribute that isn’t explicitly allowed.

ElementDefaultDetail
b, em, strong, u, i, p, ul, liAllowedGlobal attributes only
aAllowedtarget, href, title, rel, plus global attributes
imgAllowedsrc, srcset, alt, title, width, height, plus global attributes
table, td, scriptRemovedAdd tables through allowList

Global attributes, the ones allowed on every permitted element, are class, dir, id, lang, role and any aria-* attribute.

The behavior comes down to sanitize (true by default), allowList and sanitizeFn. For security reasons none of them can be set through data attributes.

const myDefaultAllowList = bootstrap.Tooltip.Default.allowList

  // allow table elements
  myDefaultAllowList.table = []

  // allow td, plus one data attribute on it
  myDefaultAllowList.td = ['data-bs-option']

A dedicated library such as DOMPurify can replace the built-in sanitizer through sanitizeFn. Bootstrap states that vulnerabilities caused solely by disabling or modifying sanitization fall outside its security model.

Styling a Bootstrap Tooltip with CSS Variables

Since v5.2.0, tooltips take their colors, width and spacing from CSS variables set on .tooltip. Override a variable under a custom class and you restyle one tooltip without touching the others.

VariableDefaultControls
–bs-tooltip-bgvar(–bs-emphasis-color)Background
–bs-tooltip-colorvar(–bs-body-bg)Text color
–bs-tooltip-max-width200pxWidth cap
–bs-tooltip-opacity.9Transparency

Plain CSS covers most changes. Scope the override with data-bs-custom-class, which the docs also describe as the customClass option.

.custom-tooltip {
  --bs-tooltip-bg: var(--bs-primary);
  --bs-tooltip-color: var(--bs-white);
}

<button type="button" class="btn btn-secondary"
data-bs-toggle="tooltip"
data-bs-custom-class="custom-tooltip"
data-bs-title="Themed with CSS variables">
Custom tooltip
</button>

Option names switch from camelCase to kebab-case in data attributes, which is why customClass becomes data-bs-custom-class.

If you need wider or multi-line text, raise --bs-tooltip-max-width past 200px.

A tooltip on a hyperlink that spans several lines gets centered, which looks odd. Add white-space: nowrap to the link to stop that.

Sass builds get the same values from $tooltip-max-width, $tooltip-opacity and related variables. $tooltip-arrow-color has been deprecated since 5.2.0 in favor of the CSS variables.

Bootstrap Tooltip Methods and Events

Tooltips expose 11 methods and 5 events. Every method is asynchronous, which means it returns when the transition starts and not when it ends.

A call made while the tooltip is mid-transition is ignored. Chain actions through events, not through back-to-back calls.

Visibility goes through show, hide and toggle. enable, disable and toggleEnabled deal with state. dispose ends an instance, and the static getInstance and getOrCreateInstance find or build one. That leaves setContent and update for content and position.

The lifecycle calls are shared by every Bootstrap plugin and together make up its programmatic API. getInstance returns null when no instance exists on the element, so getOrCreateInstance is the safer call inside event handlers.

Tooltips are enabled by default. In the v5.3.8 source, disable only flips a flag that blocks future shows, so a tooltip that’s already open stays open.

When a tooltip opens, the events fire in this order:

  1. show.bs.tooltip fires the moment show runs
  2. inserted.bs.tooltip fires once the template is in the DOM
  3. shown.bs.tooltip fires after the fade finishes

Closing mirrors it with hide.bs.tooltip and hidden.bs.tooltip. The two events that name an action in progress, show and hide, can be cancelled.

const el = document.getElementById('myTooltip')

el.addEventListener('show.bs.tooltip', event => {
  if (el.dataset.locked === 'true') event.preventDefault()
})

One catch from the JavaScript docs. When jQuery is on the page and data-bs-no-jquery isn’t set on the body, Bootstrap emits events through jQuery’s event system, and the docs say to listen with .on or .one in that case.

Calling dispose right after hide gives incorrect results, because hide returns before the transition ends. Wait for hidden instead.

el.addEventListener('hidden.bs.tooltip', () => tooltip.dispose(), { once: true })
tooltip.hide()

Tooltips created through the selector option can’t be disposed one by one on their descendants.

About the title attribute again. The tooltip documentation says a title is replaced by data-bs-title on render, but the v5.3.8 source moves the text to data-bs-original-title, removes title, and lets dispose put it back.

The source marks that attribute as kept for backwards compatibility only. If you inspect the DOM and find no title, that’s expected behavior and not a bug.

Bootstrap 4 vs Bootstrap 5 Tooltip Syntax

Attribute names, initialization, the Popper version, the arrow class and the sanitizer option name all changed between the two versions. A snippet written for one fails quietly on the other.

ItemBootstrap 4.6Bootstrap 5.3
Data attributesdata-toggle, data-placementdata-bs-toggle, data-bs-placement
Initialization$(‘[data-toggle=”tooltip”]’).tooltip()new bootstrap.Tooltip(el)
Popperv1.x (1.16.1 in the docs)v2.x (@popperjs/core 2.11.8)
Arrow class in template.arrow.tooltip-arrow
Sanitizer optionwhiteListallowList

Some defaults moved along with the syntax.

  • boundary was scrollParent and is now clippingParents
  • offset was 0 and is now [0, 6]
  • The single fallbackPlacement (default flip) became the array fallbackPlacements, default top, right, bottom, left
  • setContent and getOrCreateInstance do not appear in the 4.6 tooltip method list

The migration guide lists v5.0.0 as the release that dropped jQuery, moved Popper to v2 and namespaced every plugin attribute under data-bs-. A global find and replace on tooltip markup covers most of the move, but only on Bootstrap’s own attributes.

Sources disagree on jQuery. W3Schools’ Bootstrap 3 and 4 tooltip tutorials say tooltips must be initialized with jQuery, while the 5.3 docs say jQuery isn’t needed.

Both are right for their own version, so confirm which one you’re on before trusting either. On a v5 page, bootstrap.Tooltip.VERSION returns the plugin version.

The v4.6 documentation now opens with a banner saying Bootstrap 4 has reached end of life. New projects have no reason to start on it.

Are Bootstrap Tooltips Accessible?

Conditionally. Put tooltips only on links and form controls, and add an Escape handler, because the v5.3.8 source ships none.

Bootstrap’s own guidance limits tooltips to elements that are traditionally keyboard-focusable and interactive. Adding tabindex="0" to a plain span creates a tab stop on non-interactive content, and most assistive technologies don’t announce tooltips there.

In the v5.3.8 source, the default template gets role="tooltip". The trigger gets aria-describedby while the tooltip is shown, and it’s removed after the tooltip hides. Bootstrap also copies the title into an aria-label, but only for elements with no text and no aria-label of their own.

The aria-describedby link is how a screen reader ties the tooltip text to its trigger, and it’s one of the ARIA attributes doing the work. The aria-label part has a catch: it reads the title attribute only, so an icon-only button that uses data-bs-title gets no accessible name from Bootstrap.

WCAG 2.1 success criterion 1.4.13 (Level AA) names custom tooltips directly, according to the W3C’s Understanding document. Content shown on hover or focus has to be dismissible, hoverable and persistent, and here is how the default behavior lines up.

ConditionWCAG 1.4.13 requiresBootstrap 5.3.8 default
DismissibleCloses without moving pointer or focus, such as with EscNo Escape handler in tooltip.js
HoverablePointer can move onto the content without it vanishingHide listeners sit on the trigger only
PersistentStays until hover or focus ends, or the user dismisses itStays while the trigger is hovered or focused

That table is a reading of the source, not a compliance statement from Bootstrap, whose tooltip documentation makes no claim either way. The dismissible gap has a short fix.

const tooltipList = [...document.querySelectorAll('[data-bs-toggle="tooltip"]')]
.map(el => new bootstrap.Tooltip(el))

document.addEventListener('keydown', event => {
  if (event.key === 'Escape') tooltipList.forEach(tooltip => tooltip.hide())
})

Hoverable has no one-line fix. A click trigger sidesteps it, because the criterion covers content opened by hover or focus.

Honestly, it’s less a patch than a decision. If a page has to meet formal web accessibility requirements, keep anything a user needs to finish a task out of tooltips.

The W3C itself says there are usually more predictable means of adding content to a page, and visible help text is the plainest one. The WAI-ARIA Authoring Practices tooltip pattern is the reference to read before building anything custom.

Why Is a Bootstrap Tooltip Not Working?

Check the console first. Two of these failures throw readable errors, and the rest fail silently.

SymptomDocumented causeFix
Nothing appears, no errorTooltips were never initializedCreate a bootstrap.Tooltip per element
Error: tooltips require Popperbootstrap.js loaded without PopperUse the bundle, or load Popper first
Error: use show on visible elementsshow called on an element whose inline style is display: noneShow the element first
No tooltip on late-added elementsInitialization ran before they existedInitialize after insertion, or delegate
Disabled button ignores hoverDisabled elements get no pointer or focus eventsTrigger from a wrapper
Old attributes ignoredv5 namespaces them under data-bs-Rename the attributes
Tooltip stuck on screenElement removed while the tooltip was openHide, wait for hidden, then remove

Tooltip on a disabled button

<span class="d-inline-block" tabindex="0"
  data-bs-toggle="tooltip" data-bs-title="Disabled tooltip">
<button class="btn btn-primary" type="button" disabled>Disabled button</button>
</span>

A disabled Bootstrap button can’t be focused, hovered or clicked, so the tooltip hangs on a wrapper instead. Give the wrapper tabindex="0" so keyboard users can reach it.

The v4.6 docs add style="pointer-events: none;" to the disabled button. The v5.3 example drops it, and the v5.0.0 migration notes record that disabled buttons now have pointer-events: none by default.

Tooltips on elements added after load

You can create the instance right after the element enters the DOM. Or delegate from a parent by passing the selector option, and Bootstrap applies tooltips to matching descendants, including ones added later.

new bootstrap.Tooltip(document.body, {
  selector: '[data-bs-toggle="tooltip"]'
})

The docs warn that the title attribute must not serve as that selector. Removal follows the same rule as disposal: hide the tooltip, wait for hidden.bs.tooltip, then take the element out.

Bootstrap Tooltip FAQ

What is the difference between a Bootstrap tooltip and a popover?

A popover carries a title plus a content body, while a tooltip carries one short label.

The defaults differ as well. Popovers open on click, sit on the right and cap at 276px, whereas tooltips open on hover or focus, sit on top and cap at 200px.

Can I use a Bootstrap tooltip in React?

Not directly. The Bootstrap documentation says its JavaScript isn’t fully compatible with React, Vue or Angular, because Bootstrap and the framework may both mutate the same DOM element.

For React, the docs point to React Bootstrap, which replaces Bootstrap’s JavaScript instead of running beside it.

How do I add a tooltip to an SVG icon?

Wrap the SVG in a link and put the tooltip attributes on the link, not on the SVG.

The official example uses an anchor with the class d-inline-block, data-bs-toggle, data-bs-title and an aria-label, while the SVG inside gets aria-hidden set to true.

Can I set several tooltip options from HTML in one attribute?

Yes, through data-bs-config, an experimental attribute added in Bootstrap 5.2.0 that holds options as a JSON string.

Separate data-bs- attributes override it, and an options object passed in JavaScript overrides both.

How does a Bootstrap tooltip behave on touch screens?

The documentation doesn’t describe touch behavior, so the answer sits in the source. In v5.3.8, show adds empty mouseover listeners to the body’s children on touch-enabled devices, a workaround the code comment ties to broken event delegation on iOS.

Hide removes those listeners again.

Can I import only the tooltip instead of all of Bootstrap’s JavaScript?

Yes, with a bundler or the ES module build. The JavaScript docs demonstrate named imports using Toast from bootstrap.esm.min.js, and Tooltip follows the same pattern.

Popper still has to resolve as @popperjs/core, which an importmap handles in a browser.

What to Use When a Bootstrap Tooltip Falls Short

Information that users need to finish a task belongs in visible text, not in a Bootstrap tooltip. Keep the tooltip for optional hints on links and form controls.

When a hint outgrows one line, escalate in this order:

  • Help text printed beside the field
  • A click-triggered popover for optional detail
  • A modal dialog once the content holds links or inputs

The order tracks accessibility cost. Popover content reaches screen readers as one uninterrupted stream through aria-describedby, and popovers don’t manage focus order, per the Bootstrap 5.3 documentation.

Interactive content belongs in a modal dialog built with Bootstrap, which traps focus so every control stays reachable.

Checked against the Bootstrap 5.3.8 documentation and source on 28 October 2026. Bootstrap 5.3.8 is still the latest v5 release, and the Bootstrap blog says v5.4.0 is still to come. Bootstrap 6 reached its first alpha (6.0.0-alpha.1) on 8 October 2026 with ESM-only JavaScript and no window.bootstrap global, so the snippets here apply to 5.3. Verify tooltip dismissal and touch handling when v5.4.0 ships.