The JavaScript API: Instances, Options and Events

Advanced
13 min

The JavaScript API: Instances, Options and Events

Every Bootstrap plugin follows the same design: a class per component, one instance per DOM element, options that can come from data attributes or a constructor argument, and a set of events named {action}.bs.{component}. Once you know this pattern you can drive any component from code, integrate Bootstrap with frameworks, and fix the classic "modal opens twice" bug. This lesson lays out the shared API, then shows how it applies across components.

Loading the Plugins

Three ways, depending on the project:

html
<!-- 1. Global bundle: everything on window.bootstrap --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script> <script> const modal = new bootstrap.Modal("#example"); </script>
javascript
// 2. ES modules with a bundler import { Modal, Tooltip, Toast } from "bootstrap"; // 3. Individual plugin files for the smallest bundle import Modal from "bootstrap/js/dist/modal";

Bootstrap also ships bootstrap.esm.min.js for <script type="module"> usage without a bundler; in that case Popper must be resolved through an import map.

Constructors and Selectors

Every constructor accepts an element or a CSS selector, plus an optional options object:

javascript
new Modal(document.getElementById("example")); new Modal("#example"); new Modal("#example", { backdrop: "static" });

Options are resolved in this order: plugin defaults, then data-bs-* attributes on the element, then the constructor argument. Attribute names are the kebab-case form of the option: data-bs-auto-close="outside" equals { autoClose: "outside" }.

Getting Instances

A plugin keeps one instance per element. Creating a second one with new on the same element returns the existing instance, but the cleaner methods are:

| Method | Behavior | |---|---| | Plugin.getInstance(el) | Returns the instance or null if none exists | | Plugin.getOrCreateInstance(el, options) | Returns the instance, creating it with options if needed |

javascript
const dd = Dropdown.getInstance("#userMenu"); // null if never initialized Dropdown.getOrCreateInstance("#userMenu").show();

getOrCreateInstance is what you want almost every time; it is safe to call from any event handler without tracking state.

Common Methods

Most plugins share a small vocabulary:

  • show(), hide(), toggle(): change visibility (modal, offcanvas, collapse, dropdown, toast, tooltip, popover, tab).
  • enable(), disable(), toggleEnabled(): tooltips and popovers.
  • update(): recompute Popper position (dropdown, tooltip, popover) or modal layout after content changes.
  • dispose(): remove event listeners and data so the element can be safely deleted.
  • handleUpdate(): modal-specific re-layout when the height changes while open.

Method calls are asynchronous in the sense that they start a transition; calling show() and then hide() immediately is ignored until the transition ends.

Events

Events fire on the component's root element (the toggle for dropdowns and tabs, the modal for modals) and bubble, so delegation works. Four-event lifecycle:

| Event | Timing | Cancelable | |---|---|---| | show.bs.* | Before showing | Yes | | shown.bs.* | After the transition | No | | hide.bs.* | Before hiding | Yes | | hidden.bs.* | After hidden | No |

javascript
const modalEl = document.getElementById("editor"); modalEl.addEventListener("hide.bs.modal", (event) => { if (hasUnsavedChanges() && !window.confirm("Discard changes?")) { event.preventDefault(); // keeps the modal open } }); modalEl.addEventListener("shown.bs.modal", () => { modalEl.querySelector("input").focus(); });

Component-specific events add to this list: slide.bs.carousel / slid.bs.carousel, activate.bs.scrollspy, inserted.bs.tooltip, and hidePrevented.bs.modal when a static-backdrop modal refuses to close.

Options Reference (Selected)

| Component | Notable options | |---|---| | Modal | backdrop (true/false/"static"), keyboard, focus | | Offcanvas | backdrop, keyboard, scroll | | Tooltip / Popover | trigger, placement, html, sanitize, container, delay, customClass | | Dropdown | autoClose, boundary, display, offset, reference | | Toast | animation, autohide, delay | | Carousel | interval, ride, wrap, touch, pause | | Collapse | toggle, parent | | ScrollSpy | target, rootMargin, threshold, smoothScroll |

Read the defaults at runtime with Modal.Default.

Working with Frameworks

In React or Vue, the framework owns the DOM, so initialize plugins after mount and dispose before unmount:

javascript
// React useEffect(() => { const instance = Modal.getOrCreateInstance(ref.current); return () => instance.dispose(); }, []);

Never let the framework re-render markup that a plugin has moved or cloned (tooltips and popovers append elements to body by default). For large apps, consider the framework-specific ports of Bootstrap, but the plain API works well for a handful of components.

Sanitization

Tooltips and popovers that set html: true run content through a built-in sanitizer with an allowlist of tags and attributes. Extend it with the allowList option or disable sanitization with sanitize: false only for content you generated yourself.

Common Mistakes

  • new Modal(el) in a click handler. Repeated clicks stack instances' listeners; use getOrCreateInstance.
  • Loading both the bundle and individual modules. Handlers register twice, and components open and close in the same tick.
  • Listening on the wrong element. Dropdown and tab events fire on the toggle, not the menu or pane.
  • Removing DOM nodes without dispose(). Leaks listeners and, for tooltips, leaves orphaned elements in body.
Quick Quiz
Question 1 of 3

Which method returns an existing plugin instance or creates one if needed?

Key Takeaways

  • Each plugin is a class with one instance per element; options come from defaults, data-bs-* attributes, then the constructor.
  • Use getOrCreateInstance in handlers and dispose() before removing elements.
  • Shared methods are show, hide, toggle, update and dispose; check each component for extras.
  • Events follow show/shown/hide/hidden.bs.{component}, fire on the component's root or toggle, and the pre-transition events are cancelable.
  • Load the bundle once, or import modules once; never both.

Next lesson: RTL, Accessibility and Migrating from Bootstrap 4 - right-to-left builds, the accessibility checklist, and what changed from v4 to v5.

The JavaScript API: Instances, Options and Events - Bootstrap | CodeYourCraft | CodeYourCraft