LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2818 min read

Bootstrap JavaScript Components

Learn how Bootstrap's JavaScript components like Collapse and Offcanvas work, and the difference between data-attribute and manual JS initialization.

Introduction

Several Bootstrap components — modals, dropdowns, collapses, offcanvases, carousels, tabs, tooltips, popovers, toasts, and scrollspy — are powered by a shared JavaScript layer built on plain vanilla JS classes, one per component. You have already used many of them through data-bs-* attributes without writing a line of JavaScript. This lesson looks under the hood at how that plumbing works, and how to control the same components directly from your own scripts.

What You Will Learn
  • How the Bootstrap JS bundle discovers and wires up components.
  • The data attribute API versus the manual JavaScript API.
  • Working examples with the Collapse and Offcanvas components.
  • Listening for component lifecycle events.

How Bootstrap JS Works

Each interactive component (Modal, Collapse, Dropdown, Offcanvas, etc.) is implemented as its own small JavaScript class attached to the global bootstrap object. On page load, Bootstrap scans the DOM for elements carrying data-bs-toggle attributes and automatically instantiates the matching class for each one — that automatic scan is what makes data attributes work with zero manual setup for most components (tooltips and popovers being the deliberate exceptions covered in an earlier lesson).

Loading the JavaScript Bundle

Bootstrap ships two JS files: bootstrap.bundle.min.js, which includes Popper.js for positioning dropdowns/tooltips/popovers, and bootstrap.min.js, which does not. Unless you have Popper loaded separately, use the bundle version, placed right before the closing </body> tag.

<body>
<!-- page content -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>

Data Attribute API

The data attribute API is what you've used throughout this course: data-bs-toggle names which component to activate, and other data-bs-* attributes configure it — no JavaScript required in your own code.

<button class="btn btn-primary" type="button"
data-bs-toggle="collapse" data-bs-target="#demoPanel" aria-expanded="false"
aria-controls="demoPanel">
Toggle Panel
</button>
<div class="collapse" id="demoPanel">
<div class="card card-body">Some collapsible content.</div>
</div>

Manual JavaScript API

Every component also exposes a JavaScript constructor and instance methods, useful when you need to trigger a component from custom logic rather than a click — for example, opening a modal after a fetch request succeeds. Create an instance by passing the target DOM element, then call its methods.

const panelEl = document.getElementById('demoPanel');
const collapse = new bootstrap.Collapse(panelEl, { toggle: false });
document.getElementById('openFromCode').addEventListener('click', () => {
collapse.show();
});
document.getElementById('closeFromCode').addEventListener('click', () => {
collapse.hide();
});

Retrieve an already-initialized instance elsewhere in your code with the static getInstance() method, instead of accidentally creating a second instance on the same element.

const existing = bootstrap.Collapse.getInstance(document.getElementById('demoPanel'));
existing?.toggle();
ApproachBest For
Data attributesStatic markup, no build step, fastest to write
Manual JavaScriptTriggering components from custom logic, SPA frameworks, dynamic content

Collapse Component

Collapse toggles the visibility of content by animating its height. It can control a single target or, with an array-style selector, multiple targets—commonly used for FAQ accordions and expandable sidebars.

<button class="btn btn-outline-primary" data-bs-toggle="collapse"
data-bs-target=".multi-collapse" aria-expanded="false">
Toggle Both Panels
</button>
<div class="row">
<div class="col">
<div class="collapse multi-collapse" id="panelA">
<div class="card card-body">Panel A content</div>
</div>
</div>
<div class="col">
<div class="collapse multi-collapse" id="panelB">
<div class="card card-body">Panel B content</div>
</div>
</div>
</div>

Offcanvas Component

Offcanvas slides a panel in from the edge of the viewport, commonly used for mobile navigation drawers or filter panels. It supports the same open/close data attributes as a modal.

<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas"
data-bs-target="#filtersPanel" aria-controls="filtersPanel">
Open Filters
</button>
<div class="offcanvas offcanvas-end" tabindex="-1" id="filtersPanel">
<div class="offcanvas-header">
<h5 class="offcanvas-title">Filters</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<p>Filter controls go here.</p>
</div>
</div>

Listening to Events

Every component fires custom events on its target element around key lifecycle moments (e.g. show.bs.collapse before it opens, shown.bs.collapse after it finishes opening). Listen for these to run your own logic in sync with the component.

const panelEl = document.getElementById('demoPanel');
panelEl.addEventListener('shown.bs.collapse', () => {
console.log('Panel finished opening');
});
panelEl.addEventListener('hidden.bs.collapse', () => {
console.log('Panel finished closing');
});

Common Mistakes

Avoid These Mistakes
  • Loading bootstrap.min.js without Popper when using dropdowns, tooltips, or popovers, which silently breaks positioning.
  • Creating a new component instance with "new bootstrap.X()" on an element that already has one from the data-attribute API, leading to duplicate/conflicting instances.
  • Placing the JS <script> tag before the DOM content it targets, so elements don't exist yet when Bootstrap scans for them.
  • Forgetting aria-expanded / aria-controls on collapse and offcanvas triggers, which hurts screen reader users.

Best Practices

  • Default to the data attribute API for static markup — it is less code and self-documenting in the HTML.
  • Switch to the manual JS API only when a component needs to be triggered by custom application logic.
  • Use bootstrap.X.getInstance(el) to reuse an existing instance instead of re-instantiating.
  • Load bootstrap.bundle.min.js unless you have a specific reason to manage Popper separately.
  • Clean up event listeners and dispose of components (instance.dispose()) when removing elements dynamically in JS frameworks.

Frequently Asked Questions

No. Since Bootstrap 5, all JavaScript components are written in plain vanilla JavaScript with no jQuery dependency at all.

Yes, but framework-specific wrapper libraries (like react-bootstrap) are usually a better fit than the raw JS bundle, since they integrate more cleanly with component lifecycles and virtual DOM diffing.

It removes the component's event listeners and internal data from the element, which is important to call before removing that element from the DOM in a dynamic application to avoid memory leaks.

Key Takeaways

  • Bootstrap JS components are plain JavaScript classes exposed on the global bootstrap object.
  • data-bs-toggle attributes automatically wire up most components on page load.
  • The manual JS API (new bootstrap.X(el)) is for triggering components from your own code.
  • Components fire lifecycle events (show.bs.*, shown.bs.*, hide.bs.*, hidden.bs.*) you can hook into.

Summary

Bootstrap's JavaScript components give you two ways in: fast, zero-code data attributes for static markup, and a full JavaScript API for anything that needs custom control or integration with your own logic. Next, you'll review the common mistakes developers make with Bootstrap and the best practices that keep a project maintainable and accessible.

Next Lesson →

Common Mistakes & Best Practices