Skip to content

Documentation

On this page

Bootstrap HTML

Bootstrap 5.3.8 look and tokens, driven by native HTML instead of bootstrap.js. The widgets are usable as soon as the HTML is parsed.

What you get

23.3 KB not shipped

bootstrap.bundle.min.js is 23.3 KB gzip. That one file is every plugin, including Popper. A native widget on this site is 0 KB of component script. Per-plugin figures on each page overlap, so they are not a sum.

Ready before script

A dialog, disclosure, or popover works when the parser reaches it. Bootstrap’s plugins do nothing until bootstrap.js has downloaded, parsed, and attached listeners. That main-thread work is Total Blocking Time (TBT): long tasks between first paint and interactive. These controls add no such task, and a click does not wait for it.

0.3 KB, then only gaps

Every page loads the polyfill loader, 0.3 KB gzip. It checks the APIs and stops. The other modules are 4.9 KB gzip together, and each one downloads only when that API is missing and the page uses it. As browsers ship the features, those files stop being requested. Sizes and conditions.

2 widgets still need a small script, and only on pages that use them: alerts (0.5 KB gzip) and tabs (1.3 KB gzip). The optional behaviors — pe-focus-group, pe-toast and pe-carousel — stay unloaded until you import them.

Native HTML and CSS in a current browser. No component script. Script required for the behavior. Not a polyfill. Polyfill loader and chunks for missing APIs

Accordion
Exclusive groups with details/summary and name.
Native
Alerts
Static alerts are HTML. Dismiss needs pe-alert.
Script
Buttons
Toggle buttons are checkbox or radio plus .btn-check. No button plugin.
Native
Carousel
CSS scroll snap and swipe. Buttons outside Chrome are an optional module.
Native
Collapse
Single panel; trigger and content must be parent/child.
Native
Dropdown
Popover API menus. Polyfill if command is missing.
Native
Modal
dialog with command/commandfor. Polyfill if command or closedby is missing.
Native
Navbar
details/summary, or a dialog offcanvas. Polyfill on the dialog toggler.
Native
Offcanvas
dialog placements. Polyfill if command or closedby is missing.
Native
Popover
Titled bubbles, four directions. Polyfill if command is missing.
Native
Progress
Native progress and meter. Omit value for the indeterminate stripe.
Native
Scrollspy
scroll-target-group and :target-current. Polyfill when that CSS is missing.
Native
Spinner
aria-busy paints a spinner on the host. No extra spinner element.
Native
Tabs
pe-tabs is required. Panes do not switch without it.
Script
Toasts
popover=manual. Optional pe-toast adds data-delay and stacking.
Native
Tooltip
Hover/focus tips. aria-description is the no-script fallback. popover=hint plus interestfor is experimental.
Native
Composed examples
Mixed examples that compose existing primitives. No new pe-* element.
Native
Customizable select
appearance: base-select on .form-select. Picker, icon, and checkmark take Bootstrap tokens.
Native
Datalist
Native typeahead with input list and datalist. No dropdown script.
Native
Field sizing
Opt-in field-sizing: content. The control grows to its value.
Native
Output
Native output for a calculated result. Optional count.js for checked boxes.
Native
Range group
Two native range inputs stacked on one track. Optional range-group.js clamps and paints the fill.
Native
Spin button
Plus and minus buttons step a native input. commandfor is the input id.
Script
Validation
:user-valid / :user-invalid after type, blur, or submit. No wrapper class.
Native
Polyfills
Feature-detected fallbacks. Native browsers download only the loader.
Polyfill

Browser support

Three columns, not one baseline. Anchor positioning has no polyfill: a dropdown or popover still opens, but it is not tethered to the button in Firefox before 147 or Safari before 26.

Versions are the first release with the feature, not a test matrix.
Component No script Polyfill No fallback
Accordion details / summary. Exclusive name: Chrome 120+, Firefox 130+, Safari 17.2+. None The height transition needs interpolate-size: allow-keywords to animate block-size from 0 to auto. That is Chromium-only (Chrome 129+), so Firefox and Safari snap the panel open. ::details-content itself is Chrome 131+, Firefox 143+, Safari 18.4+, and only styles the panel.
Alerts A static .alert needs no script. Without alert.js a dismissible alert stays. command / commandfor when that API is missing. pe-alert is still required to remove the node. No native dismiss for an inline alert.
Buttons .btn-check is CSS on a native checkbox or radio. Chrome, Firefox, and Safari have shipped it with Bootstrap’s stylesheet. None None. Without this stylesheet the control is still a checkbox or radio. The label just looks like a button when .btn-check is present.
Carousel Experimental scroll-snap-type is widely available. Swipe works. None ::scroll-button and ::scroll-marker: Chrome 135+ only. Optional pe-carousel covers buttons and pips elsewhere. No autoplay, fade, or wrap.
Collapse details / summary. Trigger and panel must be parent/child. None Same interpolate-size limit as the accordion. .collapse-horizontal animates to max-content and depends on it too. The panel opens and closes everywhere.
Dropdown popover="auto": Chrome 114+, Firefox 125+, Safari 17+. command on the toggle, the same polyfill as the modal. CSS Anchor Positioning: Chrome 125+, Safari 26+, Firefox 147+. Without it the menu is not attached to the button. There is no Popper fallback.
Modal dialog command / commandfor (Chrome 135+, Firefox 144+, Safari 26.2+). closedby is missing in Safari. None
Navbar The same details menu. .navbar-expand-* shows it from that breakpoint. The offcanvas variant is a dialog. The offcanvas toggler uses the same command polyfill as the modal. The hamburger cannot sit outside the <details>. Use the offcanvas navbar for that.
Offcanvas dialog with .offcanvas-start, -end, -top, or -bottom. The same command and closedby polyfills as the modal. None
Popover popover="auto": Chrome 114+, Firefox 125+, Safari 17+. command on the trigger, the same polyfill as the modal. CSS Anchor Positioning: Chrome 125+, Safari 26+, Firefox 147+. Without it the bubble is not attached to the trigger. .bs-popover-auto does not reorient; it matches .bs-popover-end.
Progress <progress> is Baseline Widely available (2015). <meter> is too (2017). The fill follows value / max with no script. None Without this CSS the native control still works. Token colors, radius, and the striped animation need appearance: none and the vendor bar pseudos.
Scrollspy Experimental scroll-target-group: Chrome 140+. IntersectionObserver when the CSS is missing. It does not set aria-current. Marked experimental on the component page. The polyfill covers browsers without the CSS.
Spinner aria-busy is a global ARIA state. The disc is CSS on [aria-busy="true"]. Removing the attribute hides it. None Without this CSS the attribute still tells assistive technology. There is no disc. .spinner-border stays in the sheet as a standalone glyph.
Tabs The .active pane stays visible. Other tabs do not switch. None. pe-tabs is required to switch panes. It is not a polyfill. No native tab control yet. Dropdowns inside a tab list are not supported.
Toasts popover="manual" with command to show and close. No light-dismiss. None for show and close. Optional pe-toast for data-delay and stacking. Without pe-toast, open toasts share one corner.
Tooltip aria-description and content: attr(). Hover and focus. Plain text, not in the top layer. interestfor when interestForElement is missing and the page has [interestfor]. It does not polyfill popover="hint". popover="hint" is Chrome 151+, Firefox 153+, Safari preview. Without hint the polyfill cannot open the top-layer bubble. .bs-tooltip-auto on a hint popover flips with position-try-fallbacks. The arrow stays in the default (top) orientation after a flip.
Composed examples Experimental Drawer, context menu, and the progress ring work without a component script. The multi-select menu opens without script. <output data-controls> needs src/behaviors/count.js to stay in sync, and a closed popover cannot increment a CSS counter. command still uses the invoker polyfill when missing. The same command polyfill as dropdown and offcanvas. Context-menu position-area and the tab underline need CSS Anchor Positioning. The shrinking header and scroll bar need animation-timeline: scroll() (not in Firefox). The ring’s attr() into --bs-progress-value is Chrome 133+; set the custom property in markup elsewhere. No scroll-listener and no pointer-coordinate script. src/behaviors/count.js is the one optional script on this page that is not a pe-* behavior.
Customizable select Experimental appearance: base-select: Chrome 135+, Safari 27+. Firefox 149 behind dom.select.customizable_select.enabled and layout.css.appearance-base.enabled. None Marked experimental on the forms page. Without base-select the closed .form-select and the OS picker stay. Rich markup inside <option> is stripped to text. <button> / <selectedcontent> inside the select are ignored.
Datalist <datalist>: Chrome 20+, Firefox 4+, Safari 12.1+. Firefox on Android does not show the suggestion menu. None None. Without a suggestion menu the input still accepts a typed value.
Field sizing field-sizing: content: Chrome 123+, Firefox 152+, Safari 26.2+. None None. Without field-sizing the control keeps Bootstrap’s fixed box.
Output <output> is Baseline Widely available. Without count.js the text stays at whatever was in the markup. None A closed popover is display: none, so a CSS counter inside that menu stays 0. count.js is the exception for that case. Visible checkboxes can use :has() and counter() without it.
Range group Two native type="range" on one track. Keyboard, submit, and focus work. Without range-group.js thumbs can cross and there is no fill. data-range-group="off" skips the module when it is loaded. None The track is not a click target: pointer-events: none on the inputs, so only the thumbs receive pointer events. The later thumb in the DOM wins when they overlap. Fill needs range-group.js.
Spin button The input still accepts a typed or native-stepped value. Without step.js the plus and minus buttons do nothing. command / commandfor when that API is missing. step.js is still required to change the value. stepUp() throws on step="any" and on types that have no step. An empty date, time, week, or month field without min or value jumps to 1970-01-01 or 00:00.
Validation :user-valid / :user-invalid: Chrome 119+, Firefox 88+, Safari 16.5+. None None