Tooltip Benchmark

Bootstrap 5 Tooltip JS (Popper.js + TemplateFactory + Sanitizer) vs Native Popover API

Bootstrap JS

Uses bootstrap.Tooltip + Popper.js — creates DOM elements, runs createPopper() for positioning, sanitizes HTML via TemplateFactory + Sanitizer on every show.

Native Browser API

Uses Popover API (popover="manual") — element already in DOM, browser handles top-layer stacking. Show/hide via showPopover() / hidePopover(). Zero JS positioning needed.

Native Popover tooltip
Initializing... 0%

Results

Metric Bootstrap Tooltip Native Popover API
Initialization time (200 instances) - -
Toggle time (10 cycles × 200 items) - -
Instance memory cost (200 instances) - -
JS code size - -
CSS output size
_tooltip.scss (4.2 KB)
- -
Bootstrap JS 200 items — data-bs-toggle="tooltip"
Native Browser API 200 items — popover="manual"
About tooltip creation: Bootstrap Tooltip calls Popper.js’s createPopper() on every show, creates a new tooltip <div> via TemplateFactory, sanitizes its HTML content via Sanitizer, and runs JavaScript to calculate offsets and apply inline transform styles. Native Popover API tooltips are already in the DOM — showPopover() simply moves them to the top layer. Zero DOM creation, zero JS positioning, zero sanitization.
Native popover tooltip — entire implementation
HTML
<!-- Trigger -->
<button style="anchor-name: --my-tip-trigger;">
  Hover me
</button>

<!-- Tooltip (Bootstrap utility classes for styling) -->
<div popover="manual" id="my-tip"
     class="px-2 py-1 bg-dark text-white rounded small border-0 shadow"
     style="position-anchor: --my-tip-trigger;">
  Tooltip text
</div>
CSS (only positioning — no styling needed)
[popover].native-tooltip {
  inset: unset;                   /* reset UA default */
  position: fixed;
  bottom: anchor(top);            /* place above trigger */
  left: anchor(center);
  translate: -50% -4px;           /* center horizontally + gap */
}
JS (only hover listeners)
btn.addEventListener('mouseenter', () => tip.showPopover());
btn.addEventListener('mouseleave', () => tip.hidePopover());