Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Bootstrap — User Guide

Generated May 2026 from twbs/bootstrap at 5d302a0

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

Bootstrap is the most popular HTML, CSS, and JavaScript front-end framework for building responsive, mobile-first websites and web applications. It provides a comprehensive design system with a 12-column grid, dozens of pre-built components, 12 interactive JavaScript plugins, and a powerful Sass-based customization layer — all in under 30 kB of CSS and 24 kB of JS (gzipped).

Key Features

  • Responsive grid system with 6 breakpoints (xs through xxl) and flexbox-based layout
  • 12 JavaScript plugins — Alert, Button, Carousel, Collapse, Dropdown, Modal, Offcanvas, Popover, ScrollSpy, Tab, Toast, Tooltip — all usable via data attributes or JavaScript API
  • Sass-first customization — override 700+ variables, use mixins and the utilities API to generate only the CSS you need
  • Color modes — built-in light and dark theme support with automatic OS preference detection
  • RTL support — right-to-left layouts generated automatically via RTLCSS
  • No jQuery required — pure vanilla JavaScript; jQuery detected optionally via window.jQuery but never mandatory
  • Peer dependency — only @popperjs/core ^2.11.8, and only for Dropdown, Popover, and Tooltip

CDN Quickstart

Create an index.html file with this content to start using Bootstrap immediately:

<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My Bootstrap Page</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
        rel="stylesheet"
        integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
        crossorigin="anonymous">
</head>
<body>
  <div class="container py-5">
    <h1>Hello, Bootstrap 5.3.8!</h1>
    <p class="lead">You're up and running.</p>
    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#demoModal">
      Launch Demo Modal
    </button>

    <div class="modal fade" id="demoModal" tabindex="-1" aria-labelledby="demoModalLabel" aria-hidden="true">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="demoModalLabel">It Works!</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body">Bootstrap is loaded and interactive components are working.</div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <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>
</body>
</html>

Open this file in a browser. You should see a heading, a lead paragraph, and a blue "Launch Demo Modal" button. Clicking the button opens a centered modal dialog — confirming both CSS and JS are loaded correctly.

Tip

The bundle version (bootstrap.bundle.min.js) includes Popper.js, so you get Dropdowns, Popovers, and Tooltips without an extra script tag. Use this unless you have a specific reason to load Popper separately.

What to try next: Jump to Core Usage for the grid system and components, or Common Tasks for ready-to-use recipes like responsive navbars and dark mode toggles.


2. Installation

Requirements

Requirement Minimum Notes
Browser Chrome, Firefox, Edge, Safari (latest 2 major versions) IE is not supported
Node.js 14+ Only needed for npm/build workflows
Popper.js @popperjs/core ^2.11.8 Only for Dropdown, Popover, Tooltip — included in the bundle build
Sass compiler Dart Sass 1.33+ Only if customizing via SCSS

Method 1: CDN (No Build Tools)

Add these two tags to your HTML <head> and before </body>:

<!-- CSS in <head> -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
      crossorigin="anonymous">

<!-- JS before </body> -->
<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>

For RTL layouts, use the RTL CSS instead:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.rtl.min.css"
      rel="stylesheet"
      integrity="sha384-CfCrinSRH2IR6a4e6fy2q6ioOX7O6Mtm1L9vRvFZ1trBncWmMePhzvafv7oIcWiW"
      crossorigin="anonymous">

If you need separate Popper (instead of the bundle):

<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
        integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"
        integrity="sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y"
        crossorigin="anonymous"></script>

Method 2: npm

npm install bootstrap@5.3.8

Expected output:

added 2 packages in 3s

This installs Bootstrap and its peer dependency @popperjs/core. Import in your JavaScript:

// ES module import (recommended for bundlers)
import * as bootstrap from 'bootstrap';

// Or import individual plugins
import { Modal, Tooltip } from 'bootstrap';

Import the CSS in your bundler entry point or Sass file:

// Option A: Import all of Bootstrap's CSS
@import "bootstrap/dist/css/bootstrap.min.css";

// Option B: Import Bootstrap's Sass source for customization
@import "bootstrap/scss/bootstrap";

The npm package exposes these entry points:

Field Path Use Case
main dist/js/bootstrap.js CommonJS / UMD
module dist/js/bootstrap.esm.js ES modules (tree-shakeable)
sass scss/bootstrap.scss Sass source
style dist/css/bootstrap.css Pre-compiled CSS

Method 3: Composer (PHP)

composer require twbs/bootstrap:5.3.8

Method 4: NuGet (.NET)

Install-Package bootstrap -Version 5.3.8

Method 5: Source Download

Package URL
Compiled CSS & JS https://github.com/twbs/bootstrap/releases/download/v5.3.8/bootstrap-5.3.8-dist.zip
Source code https://github.com/twbs/bootstrap/archive/v5.3.8.zip
Examples https://github.com/twbs/bootstrap/releases/download/v5.3.8/bootstrap-5.3.8-examples.zip

Verification

After installation, verify Bootstrap is loaded by opening your browser's developer console and running:

// Check CSS is loaded — should return a CSSStyleSheet object
document.querySelector('link[href*="bootstrap"]')?.sheet

// Check JS is loaded — should return "5.3.8"
bootstrap.Modal.VERSION

Important

Always include the integrity and crossorigin="anonymous" attributes when loading from CDN. Subresource Integrity (SRI) protects against compromised CDN files.


3. Configuration

Sass Variable Customization

Bootstrap's design is driven by Sass variables. Override them before importing Bootstrap:

// custom.scss

// 1. Override default variables
$primary: #7952b3;
$font-family-base: 'Inter', sans-serif;
$border-radius: 0.5rem;
$enable-shadows: true;
$enable-gradients: true;
$enable-negative-margins: true;

// 2. Import Bootstrap
@import "bootstrap/scss/bootstrap";

Compile with Dart Sass:

sass custom.scss custom.css

Note

You must use Dart Sass (not LibSass or Ruby Sass). If you see deprecation warnings about mixed declarations with Dart Sass 1.80.0+, this is a known upstream issue — the warnings are cosmetic and do not affect output.

Importing Only What You Need

Reduce your CSS bundle by importing individual components:

// Required core
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";

// Optional components (pick what you use)
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api"; // Must be last — generates utility classes

Color Modes (Light / Dark / Auto)

Bootstrap 5.3.8 supports color modes via the data-bs-theme attribute on <html> or any container element:

<!-- Force light mode -->
<html data-bs-theme="light">

<!-- Force dark mode -->
<html data-bs-theme="dark">

To automatically match the OS preference, add this script before Bootstrap's CSS loads:

// Auto-detect and apply color mode
(() => {
  const stored = localStorage.getItem('theme');
  if (stored) {
    document.documentElement.setAttribute('data-bs-theme', stored);
  } else {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    document.documentElement.setAttribute('data-bs-theme', prefersDark ? 'dark' : 'light');
  }
})();

Nest color modes for per-section theming:

<html data-bs-theme="light">
  <body>
    <main>Light content here</main>
    <footer data-bs-theme="dark">
      This footer uses dark mode regardless of the page theme.
    </footer>
  </body>
</html>

Theme Colors

Bootstrap defines 8 semantic theme colors used across components:

Name Default Hex Usage
primary #0d6efd Buttons, links, active states
secondary #6c757d Muted actions, subtle elements
success #28a745 Positive feedback, confirmations
danger #dc3545 Errors, destructive actions
warning #ffc107 Caution states (dark text for contrast)
info #17a2b8 Informational highlights
light #f8f9fa Light backgrounds
dark #343a40 Dark backgrounds, emphasis

Override any color via Sass variables before the import (e.g., $primary: #7952b3;).

RTL (Right-to-Left) Support

To enable RTL layout:

  1. Set dir="rtl" on the <html> element
  2. Use the RTL CSS build instead of the default:
<html lang="ar" dir="rtl">
<head>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.rtl.min.css"
        rel="stylesheet"
        integrity="sha384-CfCrinSRH2IR6a4e6fy2q6ioOX7O6Mtm1L9vRvFZ1trBncWmMePhzvafv7oIcWiW"
        crossorigin="anonymous">
</head>

If building from Sass, set $enable-rtl: true and compile with RTLCSS in your pipeline.

jQuery Compatibility

Bootstrap 5.3.8 does not require jQuery. However, it detects jQuery automatically via window.jQuery and will use it if present. To explicitly disable jQuery integration even when jQuery is on the page, add a data attribute to the <body>:

<body data-bs-no-jquery>

For the full Sass variable reference and advanced build configuration, see the Admin Guide.


4. Core Usage

Layout

Containers

Containers are the fundamental layout wrapper. Bootstrap provides three container types:

<!-- Fixed-width container (responsive max-widths at each breakpoint) -->
<div class="container">...</div>

<!-- Full-width container (always 100%) -->
<div class="container-fluid">...</div>

<!-- Responsive container (100% until the specified breakpoint) -->
<div class="container-sm">...</div>
<div class="container-md">...</div>
<div class="container-lg">...</div>
<div class="container-xl">...</div>
<div class="container-xxl">...</div>

Container max-widths by breakpoint:

Breakpoint Abbr. Min-Width .container Max-Width
xs (none) 0 px 100% (fluid)
sm -sm 576 px 540 px
md -md 768 px 720 px
lg -lg 992 px 960 px
xl -xl 1200 px 1140 px
xxl -xxl 1400 px 1320 px

Grid System

Bootstrap uses a 12-column flexbox grid. Columns must be inside a .row, and rows must be inside a container:

<div class="container">
  <div class="row">
    <div class="col-md-8">Main content (8 of 12 columns on md+)</div>
    <div class="col-md-4">Sidebar (4 of 12 columns on md+)</div>
  </div>
</div>

Auto-width columns:

<div class="row">
  <div class="col">Equal width</div>
  <div class="col">Equal width</div>
  <div class="col">Equal width</div>
</div>

Mixed breakpoints (responsive stacking):

<div class="row">
  <!-- Full-width on xs/sm, half on md, one-third on lg+ -->
  <div class="col-12 col-md-6 col-lg-4">Card</div>
  <div class="col-12 col-md-6 col-lg-4">Card</div>
  <div class="col-12 col-md-12 col-lg-4">Card</div>
</div>

Row columns (shorthand for equal-width layouts):

<!-- 1 column on xs, 2 on md, 4 on lg -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4">
  <div class="col">Item</div>
  <div class="col">Item</div>
  <div class="col">Item</div>
  <div class="col">Item</div>
</div>

Gutters:

<!-- Custom gutter spacing -->
<div class="row g-3">...</div>      <!-- Both horizontal and vertical -->
<div class="row gx-5">...</div>     <!-- Horizontal only -->
<div class="row gy-2">...</div>     <!-- Vertical only -->
<div class="row g-0">...</div>      <!-- No gutters -->

Offsetting columns:

<div class="row">
  <div class="col-md-6 offset-md-3">Centered 6-column block</div>
</div>

Components (JavaScript Plugins)

All 12 JavaScript plugins can be used in two ways: via data attributes (no JavaScript needed) or via the JavaScript API. Every plugin fires namespaced events in the pattern action.bs.component (e.g., show.bs.modal, hidden.bs.collapse).

Alert

Dismissible alerts with a close button:

<div class="alert alert-warning alert-dismissible fade show" role="alert">
  <strong>Warning!</strong> Check your input before submitting.
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>

Via JavaScript:

const alertEl = document.querySelector('.alert');
const bsAlert = bootstrap.Alert.getOrCreateInstance(alertEl);
bsAlert.close();

Events: close.bs.alert, closed.bs.alert.

Button

Toggle active state on buttons:

<button type="button" class="btn btn-primary" data-bs-toggle="button">Toggle Me</button>

Via JavaScript:

const btn = document.querySelector('.btn');
const bsButton = bootstrap.Button.getOrCreateInstance(btn);
bsButton.toggle();

Image slideshow with controls and indicators:

<div id="myCarousel" class="carousel slide" data-bs-ride="carousel">
  <div class="carousel-indicators">
    <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
    <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>
    <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>
  </div>
  <div class="carousel-inner">
    <div class="carousel-item active">
      <img src="slide1.jpg" class="d-block w-100" alt="First slide">
      <div class="carousel-caption d-none d-md-block">
        <h5>First Slide</h5>
        <p>Description here.</p>
      </div>
    </div>
    <div class="carousel-item">
      <img src="slide2.jpg" class="d-block w-100" alt="Second slide">
    </div>
    <div class="carousel-item">
      <img src="slide3.jpg" class="d-block w-100" alt="Third slide">
    </div>
  </div>
  <button class="carousel-control-prev" type="button" data-bs-target="#myCarousel" data-bs-slide="prev">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Previous</span>
  </button>
  <button class="carousel-control-next" type="button" data-bs-target="#myCarousel" data-bs-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>
</div>

Carousel defaults:

Option Default Description
interval 5000 Time (ms) between auto-cycling slides
keyboard true React to keyboard arrow keys
pause 'hover' Pause on mouse hover (false to disable)
ride false Auto-play on load ('carousel' to enable)
touch true Support swipe on touch devices
wrap true Cycle continuously or stop at last slide

Events: slide.bs.carousel, slid.bs.carousel.

Collapse

Toggle visibility of content:

<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
  Toggle Content
</a>
<div class="collapse" id="collapseExample">
  <div class="card card-body">
    This content is collapsible.
  </div>
</div>

Collapse defaults:

Option Default Description
parent null Selector for accordion behavior (only one open at a time)
toggle true Toggle on initialization

Events: show.bs.collapse, shown.bs.collapse, hide.bs.collapse, hidden.bs.collapse.

Toggleable menus for navigation and actions:

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
    Dropdown
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Action</a></li>
    <li><a class="dropdown-item" href="#">Another action</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a class="dropdown-item" href="#">Separated link</a></li>
  </ul>
</div>

Dropdown defaults:

Option Default Description
autoClose true Close behavior: true, 'inside', 'outside', false
boundary 'clippingParents' Overflow constraint boundary for Popper
display 'dynamic' 'dynamic' (Popper positioning) or 'static'
offset [0, 2] [skidding, distance] in pixels
popperConfig null Custom Popper.js configuration object
reference 'toggle' Reference element: 'toggle', 'parent', or an HTMLElement

Note

Dropdowns require Popper.js. Use the bundle build (bootstrap.bundle.min.js) or load Popper separately before Bootstrap's JS.

Events: show.bs.dropdown, shown.bs.dropdown, hide.bs.dropdown, hidden.bs.dropdown.

Dialogs for confirmations, forms, and content:

<!-- Trigger -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
  Open Modal
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal Title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        Modal content goes here.
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary">Save</button>
      </div>
    </div>
  </div>
</div>

Modal defaults:

Option Default Description
backdrop true Show backdrop; 'static' prevents close on backdrop click
focus true Focus the modal when opened
keyboard true Close on Escape key

Modal sizes: .modal-sm, .modal-lg, .modal-xl. Fullscreen: .modal-fullscreen or responsive variants like .modal-fullscreen-lg-down.

Via JavaScript:

const modal = new bootstrap.Modal('#exampleModal', {
  backdrop: 'static',
  keyboard: false
});
modal.show();

// Listen for events
document.getElementById('exampleModal').addEventListener('hidden.bs.modal', () => {
  console.log('Modal closed');
});

Events: show.bs.modal, shown.bs.modal, hide.bs.modal, hidden.bs.modal, hidePrevented.bs.modal.

Offcanvas

Slide-in side panels (sidebars, mobile navigation):

<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasExample" aria-controls="offcanvasExample">
  Open Sidebar
</button>

<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
  <div class="offcanvas-header">
    <h5 class="offcanvas-title" id="offcanvasExampleLabel">Sidebar</h5>
    <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
  </div>
  <div class="offcanvas-body">
    Sidebar content here.
  </div>
</div>

Offcanvas defaults:

Option Default Description
backdrop true Show backdrop behind offcanvas
keyboard true Close on Escape key
scroll false Allow body scrolling while offcanvas is open

Placement classes: .offcanvas-start (left), .offcanvas-end (right), .offcanvas-top, .offcanvas-bottom.

Events: show.bs.offcanvas, shown.bs.offcanvas, hide.bs.offcanvas, hidden.bs.offcanvas, hidePrevented.bs.offcanvas.

Popover

Rich content tooltips (requires Popper.js):

<button type="button" class="btn btn-lg btn-danger"
        data-bs-toggle="popover"
        data-bs-title="Popover Title"
        data-bs-content="This is the popover body content.">
  Click to toggle popover
</button>

Initialize all popovers on the page:

const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(el => new bootstrap.Popover(el));

Warning

Popovers are opt-in — they are not initialized automatically. You must initialize them via JavaScript as shown above, even when using data attributes for configuration.

Events: show.bs.popover, shown.bs.popover, hide.bs.popover, hidden.bs.popover, inserted.bs.popover.

ScrollSpy

Automatically update navigation based on scroll position:

<nav id="navbar-example" class="navbar navbar-light bg-light px-3">
  <a class="navbar-brand" href="#">Navbar</a>
  <nav class="nav nav-pills">
    <a class="nav-link" href="#section1">Section 1</a>
    <a class="nav-link" href="#section2">Section 2</a>
    <a class="nav-link" href="#section3">Section 3</a>
  </nav>
</nav>

<div data-bs-spy="scroll" data-bs-target="#navbar-example" data-bs-offset="10" tabindex="0"
     style="height: 300px; overflow-y: scroll;">
  <h4 id="section1">Section 1</h4>
  <p>Content...</p>
  <h4 id="section2">Section 2</h4>
  <p>Content...</p>
  <h4 id="section3">Section 3</h4>
  <p>Content...</p>
</div>

Events: activate.bs.scrollspy.

Tab

Tabbed content panels:

<ul class="nav nav-tabs" role="tablist">
  <li class="nav-item" role="presentation">
    <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#home" type="button" role="tab" aria-controls="home" aria-selected="true">Home</button>
  </li>
  <li class="nav-item" role="presentation">
    <button class="nav-link" data-bs-toggle="tab" data-bs-target="#profile" type="button" role="tab" aria-controls="profile" aria-selected="false">Profile</button>
  </li>
</ul>
<div class="tab-content">
  <div class="tab-pane fade show active" id="home" role="tabpanel">Home content</div>
  <div class="tab-pane fade" id="profile" role="tabpanel">Profile content</div>
</div>

Events: show.bs.tab, shown.bs.tab, hide.bs.tab, hidden.bs.tab.

Toast

Lightweight, dismissible notification messages:

<div class="toast-container position-fixed bottom-0 end-0 p-3">
  <div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
    <div class="toast-header">
      <strong class="me-auto">Notification</strong>
      <small>Just now</small>
      <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
    </div>
    <div class="toast-body">
      Your changes have been saved.
    </div>
  </div>
</div>

Toasts must be initialized via JavaScript:

const toastEl = document.getElementById('liveToast');
const toast = bootstrap.Toast.getOrCreateInstance(toastEl);
toast.show();

Events: show.bs.toast, shown.bs.toast, hide.bs.toast, hidden.bs.toast.

Tooltip

Hover hints (requires Popper.js):

<button type="button" class="btn btn-secondary"
        data-bs-toggle="tooltip" data-bs-placement="top"
        data-bs-title="Tooltip on top">
  Hover me
</button>

Initialize all tooltips:

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

Warning

Like Popovers, Tooltips are opt-in and must be initialized via JavaScript. They are not activated by data attributes alone.

Events: show.bs.tooltip, shown.bs.tooltip, hide.bs.tooltip, hidden.bs.tooltip, inserted.bs.tooltip.

Forms

Bootstrap provides styled form controls, layout options, and validation:

<form class="needs-validation" novalidate>
  <div class="mb-3">
    <label for="email" class="form-label">Email address</label>
    <input type="email" class="form-control" id="email" required>
    <div class="invalid-feedback">Please enter a valid email.</div>
  </div>
  <div class="mb-3">
    <label for="password" class="form-label">Password</label>
    <input type="password" class="form-control" id="password" required minlength="8">
    <div class="invalid-feedback">Password must be at least 8 characters.</div>
  </div>
  <div class="mb-3 form-check">
    <input type="checkbox" class="form-check-input" id="terms" required>
    <label class="form-check-label" for="terms">Accept terms</label>
    <div class="invalid-feedback">You must agree before submitting.</div>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

Form control classes:

Element Class Notes
Text inputs .form-control Also for textarea, email, password, etc.
Select .form-select Replaces default select styling
Checkbox / Radio .form-check-input Wrap in .form-check
Range .form-range Custom range slider
File input .form-control Styled file picker
Floating labels .form-floating Wrap around input + label
Input group .input-group Prepend/append text or buttons

Typography

<!-- Display headings (larger, bolder) -->
<h1 class="display-1">Display 1</h1>

<!-- Lead paragraph -->
<p class="lead">This is a lead paragraph with larger text.</p>

<!-- Inline text elements -->
<mark>Highlighted</mark>
<del>Deleted</del>
<s>Strikethrough</s>
<small>Fine print</small>

<!-- Blockquote -->
<figure>
  <blockquote class="blockquote">
    <p>A well-known quote.</p>
  </blockquote>
  <figcaption class="blockquote-footer">Someone in <cite title="Source Title">Source Title</cite></figcaption>
</figure>

Utilities

Bootstrap generates dozens of utility classes for rapid styling:

<!-- Spacing: {property}{sides}-{breakpoint}-{size} -->
<div class="mt-3 mb-5 px-4">Margin top 3, margin bottom 5, padding x 4</div>

<!-- Flexbox -->
<div class="d-flex justify-content-between align-items-center">...</div>

<!-- Colors -->
<p class="text-primary bg-light p-3">Primary text on light background</p>

<!-- Borders -->
<div class="border border-primary rounded-3">Bordered element</div>

<!-- Visibility -->
<div class="d-none d-md-block">Hidden on mobile, visible on md+</div>
<div class="d-block d-md-none">Visible on mobile, hidden on md+</div>

<!-- Position -->
<div class="position-fixed top-0 start-0">Fixed top-left</div>

<!-- Shadow -->
<div class="shadow-sm">Small shadow</div>
<div class="shadow">Regular shadow</div>
<div class="shadow-lg">Large shadow</div>

5. Common Tasks

Responsive Navbar with Mobile Hamburger

<nav class="navbar navbar-expand-lg bg-body-tertiary">
  <div class="container">
    <a class="navbar-brand" href="#">MySite</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
            aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav me-auto">
        <li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Home</a></li>
        <li class="nav-item"><a class="nav-link" href="#">Features</a></li>
        <li class="nav-item"><a class="nav-link" href="#">Pricing</a></li>
      </ul>
      <form class="d-flex" role="search">
        <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
        <button class="btn btn-outline-primary" type="submit">Search</button>
      </form>
    </div>
  </div>
</nav>

This navbar is horizontal on lg and above, and collapses to a hamburger menu on smaller screens.

<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#contactModal">
  Contact Us
</button>

<div class="modal fade" id="contactModal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Contact Form</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <form id="contactForm">
          <div class="mb-3">
            <label for="contactName" class="form-label">Name</label>
            <input type="text" class="form-control" id="contactName" required>
          </div>
          <div class="mb-3">
            <label for="contactEmail" class="form-label">Email</label>
            <input type="email" class="form-control" id="contactEmail" required>
          </div>
          <div class="mb-3">
            <label for="contactMessage" class="form-label">Message</label>
            <textarea class="form-control" id="contactMessage" rows="3" required></textarea>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
        <button type="submit" form="contactForm" class="btn btn-primary">Send</button>
      </div>
    </div>
  </div>
</div>
<div id="gallery" class="carousel slide" data-bs-ride="carousel" data-bs-interval="3000">
  <div class="carousel-inner rounded-3 shadow">
    <div class="carousel-item active">
      <img src="photo1.jpg" class="d-block w-100" alt="Photo 1">
    </div>
    <div class="carousel-item">
      <img src="photo2.jpg" class="d-block w-100" alt="Photo 2">
    </div>
    <div class="carousel-item">
      <img src="photo3.jpg" class="d-block w-100" alt="Photo 3">
    </div>
  </div>
  <button class="carousel-control-prev" type="button" data-bs-target="#gallery" data-bs-slide="prev">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Previous</span>
  </button>
  <button class="carousel-control-next" type="button" data-bs-target="#gallery" data-bs-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>
</div>

Dark Mode Toggle Button

<button id="themeToggle" class="btn btn-outline-secondary">
  <span id="themeIcon">🌙</span> Toggle Theme
</button>

<script>
  const toggle = document.getElementById('themeToggle');
  const icon = document.getElementById('themeIcon');

  toggle.addEventListener('click', () => {
    const current = document.documentElement.getAttribute('data-bs-theme');
    const next = current === 'dark' ? 'light' : 'dark';
    document.documentElement.setAttribute('data-bs-theme', next);
    localStorage.setItem('theme', next);
    icon.textContent = next === 'dark' ? '☀️' : '🌙';
  });

  // Restore saved preference on load
  const saved = localStorage.getItem('theme') ||
    (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  document.documentElement.setAttribute('data-bs-theme', saved);
  icon.textContent = saved === 'dark' ? '☀️' : '🌙';
</script>

Custom Theme via Sass

// mytheme.scss
$primary: #6f42c1;      // Purple
$secondary: #20c997;    // Teal
$font-family-base: 'Poppins', sans-serif;
$border-radius: 1rem;
$enable-shadows: true;

@import "bootstrap/scss/bootstrap";

Compile:

sass mytheme.scss mytheme.css

Client-Side Form Validation

// Apply Bootstrap validation styles
(() => {
  'use strict';
  const forms = document.querySelectorAll('.needs-validation');
  Array.from(forms).forEach(form => {
    form.addEventListener('submit', event => {
      if (!form.checkValidity()) {
        event.preventDefault();
        event.stopPropagation();
      }
      form.classList.add('was-validated');
    }, false);
  });
})();

Add novalidate to the <form> and required, minlength, pattern, etc. to inputs. Bootstrap applies .is-valid / .is-invalid styling when the .was-validated class is on the form.

Toast Notification System

function showToast(message, type = 'primary') {
  const container = document.querySelector('.toast-container') || (() => {
    const div = document.createElement('div');
    div.className = 'toast-container position-fixed bottom-0 end-0 p-3';
    document.body.appendChild(div);
    return div;
  })();

  const toastHTML = `
    <div class="toast align-items-center text-bg-${type} border-0" role="alert" aria-live="assertive" aria-atomic="true">
      <div class="d-flex">
        <div class="toast-body">${message}</div>
        <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
      </div>
    </div>`;

  container.insertAdjacentHTML('beforeend', toastHTML);
  const toastEl = container.lastElementChild;
  const toast = new bootstrap.Toast(toastEl, { delay: 5000 });
  toast.show();

  toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove());
}

// Usage
showToast('Settings saved successfully!', 'success');
showToast('Something went wrong.', 'danger');
<body class="d-flex flex-column min-vh-100">
  <nav class="navbar navbar-expand-lg bg-body-tertiary">
    <!-- Navbar content -->
  </nav>

  <main class="container flex-grow-1 py-4">
    <!-- Page content -->
  </main>

  <footer class="bg-body-tertiary py-3 mt-auto">
    <div class="container text-center">
      <span class="text-body-secondary">&copy; 2026 My Company</span>
    </div>
  </footer>
</body>

The min-vh-100 on body plus flex-grow-1 on main ensures the footer stays at the bottom even on short pages.

<ul class="nav nav-pills">
  <li class="nav-item">
    <a class="nav-link active" href="#">Home</a>
  </li>
  <li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">
      Products
    </a>
    <ul class="dropdown-menu">
      <li><a class="dropdown-item" href="#">Software</a></li>
      <li><a class="dropdown-item" href="#">Hardware</a></li>
      <li><hr class="dropdown-divider"></li>
      <li><a class="dropdown-item" href="#">All Products</a></li>
    </ul>
  </li>
  <li class="nav-item">
    <a class="nav-link" href="#">About</a>
  </li>
</ul>

Accordion FAQ

<div class="accordion" id="faqAccordion">
  <div class="accordion-item">
    <h2 class="accordion-header">
      <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1" aria-expanded="true" aria-controls="faq1">
        What is Bootstrap?
      </button>
    </h2>
    <div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#faqAccordion">
      <div class="accordion-body">
        Bootstrap is a front-end framework for building responsive, mobile-first websites.
      </div>
    </div>
  </div>
  <div class="accordion-item">
    <h2 class="accordion-header">
      <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2" aria-expanded="false" aria-controls="faq2">
        Is Bootstrap free?
      </button>
    </h2>
    <div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
      <div class="accordion-body">
        Yes. Bootstrap is released under the MIT License and is free for personal and commercial use.
      </div>
    </div>
  </div>
</div>

The data-bs-parent="#faqAccordion" ensures only one item is open at a time (accordion behavior via the Collapse plugin with a parent option).

Responsive Card Grid

<div class="container">
  <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
    <div class="col">
      <div class="card h-100">
        <img src="card1.jpg" class="card-img-top" alt="Card 1">
        <div class="card-body">
          <h5 class="card-title">Card Title</h5>
          <p class="card-text">Card description goes here.</p>
        </div>
        <div class="card-footer">
          <a href="#" class="btn btn-primary">Read More</a>
        </div>
      </div>
    </div>
    <!-- Repeat for more cards -->
  </div>
</div>

The h-100 class ensures all cards in a row are equal height. row-cols-* sets the number of columns per row at each breakpoint.

Offcanvas Mobile Sidebar

<button class="btn btn-primary d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar">
  Menu
</button>

<div class="offcanvas-lg offcanvas-start" tabindex="-1" id="sidebar">
  <div class="offcanvas-header">
    <h5 class="offcanvas-title">Menu</h5>
    <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
  </div>
  <div class="offcanvas-body">
    <nav class="nav flex-column">
      <a class="nav-link active" href="#">Dashboard</a>
      <a class="nav-link" href="#">Orders</a>
      <a class="nav-link" href="#">Products</a>
      <a class="nav-link" href="#">Customers</a>
    </nav>
  </div>
</div>

The .offcanvas-lg class makes this a slide-in panel on screens smaller than lg, and a static visible sidebar on lg and above.


6. Troubleshooting

Diagnostic Decision Tree

Component not working?
├── CSS loaded?
│   ├── No  → Add Bootstrap CSS <link> to <head>
│   └── Yes → JS loaded?
│       ├── No  → Add Bootstrap JS before </body>
│       └── Yes → Using Popper-dependent component? (Dropdown/Popover/Tooltip)
│           ├── Yes → Using bundle.min.js or separate Popper?
│           │   ├── Neither → Add bundle.min.js or load Popper first
│           │   └── Both loaded → Check initialization (Popovers/Tooltips are opt-in)
│           └── No  → Check data attributes (must use data-bs-* prefix, not data-*)
│               └── Still broken? → Check console for JS errors
│                   └── Multiple Bootstrap versions loaded? Remove duplicates.

Error: Uncaught TypeError: bootstrap.Modal is not a constructor

Cause: Bootstrap JS is not loaded, or you are loading the ESM build in a non-module script tag.

Solution: Ensure the script tag is present and uses the UMD build:

<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>

If using the ESM build, the script tag must have type="module":

<script type="module">
  import { Modal } from 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.esm.min.js';
</script>

Prevention: Use the bundle UMD build for simple projects; reserve ESM for bundler-based setups.

Error: Popper is required for dropdowns/tooltips/popovers

Cause: You loaded bootstrap.min.js (which does not include Popper) without loading Popper separately.

Solution: Either switch to the bundle build or add Popper before Bootstrap:

<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
        integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"
        integrity="sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y"
        crossorigin="anonymous"></script>

Prevention: Default to bootstrap.bundle.min.js unless you have a specific reason to exclude Popper.

Tooltips and Popovers Not Appearing

Cause: These plugins are opt-in and not automatically initialized from data attributes alone.

Solution: Initialize them explicitly via JavaScript:

// Initialize all tooltips
document.querySelectorAll('[data-bs-toggle="tooltip"]')
  .forEach(el => new bootstrap.Tooltip(el));

// Initialize all popovers
document.querySelectorAll('[data-bs-toggle="popover"]')
  .forEach(el => new bootstrap.Popover(el));

Prevention: Add initialization code in a DOMContentLoaded listener or at the end of <body>.

Error: TypeError When Popover data-bs-content Is "true" or "false"

Cause: When data-bs-content is set to the string "true" or "false", Bootstrap's attribute parsing auto-converts it to a boolean, causing a TypeError when the Popover plugin tries to use it as string content. This is a known issue (GitHub #41925).

Solution: Wrap the value in extra characters to prevent boolean coercion:

<!-- Instead of data-bs-content="true" -->
<button data-bs-toggle="popover" data-bs-content="Value: true">Info</button>

Or set content via JavaScript:

new bootstrap.Popover(el, { content: 'true' });

Prevention: Avoid using bare "true" or "false" as popover content strings.

Cause: Dark dropdowns and navbars may not automatically inherit dark-mode CSS custom properties when the data-bs-theme="dark" attribute is set at the page level. This is a known styling gap (GitHub #39984).

Solution: Explicitly add the data-bs-theme="dark" attribute directly on the dropdown menu or navbar element:

<div class="dropdown-menu" data-bs-theme="dark">
  <!-- items -->
</div>

Prevention: When using dark mode, test dropdown and navbar components explicitly and apply data-bs-theme at the component level if the page-level attribute is not inherited.

Dart Sass 1.80.0+ Deprecation Warnings

Cause: Dart Sass 1.80.0 and newer emit deprecation warnings for mixed declarations — a pattern used in Bootstrap's Sass source. This is cosmetic and does not affect the compiled CSS output (GitHub #40962).

Solution: Suppress the warnings in your build tool:

sass --quiet-deps custom.scss custom.css

Or in a bundler configuration (e.g., webpack):

// webpack.config.js
{
  loader: 'sass-loader',
  options: {
    sassOptions: {
      quietDeps: true
    }
  }
}

Prevention: Pin Dart Sass below 1.80.0 if warnings are disruptive, or use --quiet-deps in your build scripts.

Missing aria-pressed="false" on Toggle Buttons

Cause: Toggle buttons initialized via data-bs-toggle="button" do not automatically set aria-pressed="false" at initialization — only aria-pressed="true" is set when toggled on (GitHub #42328).

Solution: Manually add the attribute in your HTML:

<button type="button" class="btn btn-primary" data-bs-toggle="button" aria-pressed="false">
  Toggle
</button>

Prevention: Always include aria-pressed="false" on toggle buttons for screen reader compatibility.

Cause: Programmatically calling modal.hide() before the show transition finishes, or removing the modal element from the DOM while it is still visible.

Solution: Listen for the shown.bs.modal event before calling hide, and use dispose() before removing from DOM:

const modal = bootstrap.Modal.getInstance(element);
modal.hide();
element.addEventListener('hidden.bs.modal', () => {
  modal.dispose();
  element.remove();
});

Prevention: Always wait for transition events to complete before programmatically manipulating modals.

Grid Columns Not Stacking on Mobile

Cause: Using only col-lg-6 without a mobile fallback means columns are auto-sized (not full-width) on smaller screens.

Solution: Add col-12 as the base and layer on responsive overrides:

<div class="row">
  <div class="col-12 col-lg-6">Full on mobile, half on desktop</div>
  <div class="col-12 col-lg-6">Full on mobile, half on desktop</div>
</div>

Prevention: Always start with the smallest breakpoint and add responsive classes upward (mobile-first).

JavaScript Plugin Instances Leaking Memory

Cause: Calling new bootstrap.Modal(el) multiple times on the same element creates duplicate instances.

Solution: Use getOrCreateInstance() instead of the constructor:

// Correct: reuses existing instance
const modal = bootstrap.Modal.getOrCreateInstance(el);

// Also correct: check first
const existing = bootstrap.Modal.getInstance(el);
if (!existing) {
  new bootstrap.Modal(el);
}

Prevention: Use getOrCreateInstance() as your default pattern for all plugin initialization.

Data Attributes Using Wrong Prefix

Cause: Using data-toggle or data-dismiss (Bootstrap 4 syntax) instead of the data-bs-* prefix required by Bootstrap 5.

Solution: Add the bs- infix to all data attributes:

<!-- Wrong (Bootstrap 4) -->
<button data-toggle="modal" data-target="#myModal">Open</button>

<!-- Correct (Bootstrap 5) -->
<button data-bs-toggle="modal" data-bs-target="#myModal">Open</button>

Prevention: Search your codebase for data-toggle and data-dismiss and replace with data-bs-toggle and data-bs-dismiss.


7. FAQ

General

Does Bootstrap require jQuery? No. Bootstrap 5.3.8 is written in vanilla JavaScript. If jQuery is present on the page, Bootstrap detects it via window.jQuery and uses it automatically. You can disable this by adding data-bs-no-jquery to the <body> tag.

What license is Bootstrap released under? MIT License. It is free for personal and commercial use with no restrictions.

What is the current version? 5.3.8. You can verify at runtime with bootstrap.Modal.VERSION (or any plugin's .VERSION property), which returns "5.3.8".

How large is Bootstrap? The core CSS is under 30.25 kB gzipped (bootstrap.min.css), the JS bundle (including Popper) is under 23.5 kB gzipped (bootstrap.bundle.min.js), and the standalone JS is under 16.25 kB gzipped (bootstrap.min.js).

Installation & Setup

Can I use Bootstrap without npm or a build tool? Yes. The CDN method requires zero build tools — just two HTML tags (one <link> for CSS, one <script> for JS). See Quick Start.

How do I include only the components I need? Import individual Sass files instead of the full bootstrap.scss. For JavaScript, use ES module imports to pull in only the plugins you need (bundlers will tree-shake unused code):

import { Modal, Tooltip } from 'bootstrap';

Do I need Popper.js? Only if you use Dropdown, Popover, or Tooltip. The bootstrap.bundle.min.js build includes Popper. If you don't use those three components, use bootstrap.min.js instead and skip Popper entirely.

Customization

How do I change the primary color? Set $primary before importing Bootstrap in your Sass file:

$primary: #7952b3;
@import "bootstrap/scss/bootstrap";

How do I enable dark mode? Add data-bs-theme="dark" to the <html> element. For automatic OS-preference detection, see the dark mode toggle recipe in Common Tasks.

Can I use Bootstrap with Tailwind CSS? Technically possible but not recommended. Both frameworks generate utility classes that will conflict (e.g., .container, .flex, .hidden). Choose one framework per project.

Components

Why aren't my Tooltips/Popovers working? They require explicit JavaScript initialization. Add this after Bootstrap JS loads:

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

How do I prevent the modal from closing when clicking outside? Set backdrop to 'static':

<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">

What is the difference between bootstrap.min.js and bootstrap.bundle.min.js? The bundle includes Popper.js (~7 kB). Use the bundle unless you load Popper separately or don't need Dropdown, Popover, or Tooltip components.

How do I listen for component events? All plugins fire namespaced DOM events. Listen with standard addEventListener:

document.getElementById('myModal').addEventListener('shown.bs.modal', (event) => {
  console.log('Modal is now visible');
});

Migration & Compatibility

Caution

If migrating from Bootstrap 4, the most common breaking change is the data attribute prefix. All data-* attributes are now data-bs-*. A global find-and-replace of data-toggle to data-bs-toggle and data-dismiss to data-bs-dismiss addresses most issues.

Does Bootstrap support IE 11? No. Bootstrap 5 dropped Internet Explorer support entirely. The last version supporting IE 11 is Bootstrap 4.6.x.

Can I use Bootstrap with React/Vue/Angular? Yes, but consider using framework-specific wrappers like react-bootstrap or ng-bootstrap that replace Bootstrap's JS with native framework components. If using Bootstrap's JS directly, initialize plugins in lifecycle hooks (e.g., useEffect in React, onMounted in Vue) and call dispose() on cleanup.