Bootstrap 5 dropped jQuery and now ships its own vanilla JavaScript component library. Understanding how to use it programmatically unlocks the full power of Bootstrap — you can open modals from API calls, trigger tooltips on custom events and respond to component lifecycle hooks.

Two Ways to Use Bootstrap JS

Bootstrap 5 gives you two approaches that can be mixed freely.

Data attributes — declarative, no JavaScript needed:

<button data-bs-toggle="modal" data-bs-target="#myModal">Open</button>

JavaScript API — imperative, full programmatic control:

const modal = new bootstrap.Modal(document.getElementById('myModal'))
modal.show()

Both are valid. Data attributes are great for simple cases. The JS API is essential when you need to trigger components based on application logic — after an API call, on a custom event, based on user state.

Loading Bootstrap JS

CDN bundle (includes Popper):

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

ES module import (with bundler):

// Import everything
import * as bootstrap from 'bootstrap'

// Import only what you need (better for bundle size)
import { Modal, Tooltip, Dropdown, Toast, Offcanvas } from 'bootstrap'

Modal API

const modalEl = document.getElementById('myModal')

// Create instance
const modal = new bootstrap.Modal(modalEl, {
  backdrop: true,        // true, false, or 'static'
  keyboard: true,        // close on Escape key
  focus: true            // autofocus on open
})

// Methods
modal.show()
modal.hide()
modal.toggle()
modal.dispose()          // destroy instance and listeners

// Get existing instance (returns null if none)
const existing = bootstrap.Modal.getInstance(modalEl)

// Get or create instance
const instance = bootstrap.Modal.getOrCreateInstance(modalEl)
instance.show()

Real-world example — open a modal after a successful API call:

async function saveTemplate(data) {
  try {
    const response = await fetch('/api/templates', {
      method: 'POST',
      body: JSON.stringify(data)
    })

    if (response.ok) {
      // Show success modal programmatically
      const successModal = bootstrap.Modal.getOrCreateInstance(
        document.getElementById('successModal')
      )
      successModal.show()
    }
  } catch (err) {
    console.error('Save failed:', err)
  }
}

Modal Events

const modalEl = document.getElementById('myModal')

// Before show (cancelable)
modalEl.addEventListener('show.bs.modal', function(e) {
  console.log('Modal about to show')
  // e.preventDefault() to cancel
})

// After show animation completes
modalEl.addEventListener('shown.bs.modal', function() {
  // Focus an input inside
  document.getElementById('modalInput').focus()
})

// Before hide
modalEl.addEventListener('hide.bs.modal', function() {
  console.log('Modal about to close')
})

// After hide animation completes
modalEl.addEventListener('hidden.bs.modal', function() {
  // Reset form when modal closes
  document.getElementById('myForm').reset()
})

Toast API

const toastEl = document.getElementById('myToast')

const toast = new bootstrap.Toast(toastEl, {
  autohide: true,
  delay: 3000        // ms before auto-dismiss
})

toast.show()
toast.hide()

// Show a toast after saving
async function save() {
  await fetch('/api/save', { method: 'POST' })
  bootstrap.Toast.getOrCreateInstance(
    document.getElementById('saveToast')
  ).show()
}

Creating toasts dynamically:

function showToast(message, type = 'success') {
  // Create toast element on the fly
  const toastEl = document.createElement('div')
  toastEl.className = `toast align-items-center text-white border-0`
  toastEl.style.background = type === 'success' ? '#10b981' : '#fd4766'
  toastEl.innerHTML = `
    <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"></button>
    </div>
  `

  // Add to toast container
  document.getElementById('toastContainer').appendChild(toastEl)

  const toast = new bootstrap.Toast(toastEl, { delay: 3000 })
  toast.show()

  // Clean up after hiding
  toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove())
}

// Usage
showToast('Template saved successfully!')
showToast('Upload failed — try again', 'error')

Dropdown API

const dropdownEl = document.getElementById('myDropdown')
const dropdown = new bootstrap.Dropdown(dropdownEl, {
  autoClose: true,     // true, false, 'inside', 'outside'
  offset: [0, 8],      // [skidding, distance]
  popperConfig: {}     // custom Popper config
})

dropdown.show()
dropdown.hide()
dropdown.toggle()
dropdown.update()      // reposition if page changed

// Events
dropdownEl.addEventListener('show.bs.dropdown', () => console.log('Opening'))
dropdownEl.addEventListener('shown.bs.dropdown', () => console.log('Opened'))
dropdownEl.addEventListener('hide.bs.dropdown', () => console.log('Closing'))

Tooltip API

Tooltips must be initialised via JavaScript — they don't activate from data attributes alone:

// Initialise all tooltips on page
const tooltipEls = document.querySelectorAll('[data-bs-toggle="tooltip"]')
tooltipEls.forEach(el => new bootstrap.Tooltip(el))

// Single tooltip with options
const tooltip = new bootstrap.Tooltip(document.getElementById('myBtn'), {
  title: 'Click to copy',
  placement: 'top',    // top, bottom, left, right, auto
  trigger: 'hover',    // hover, click, focus, manual
  delay: { show: 200, hide: 100 }
})

// Programmatic control
tooltip.show()
tooltip.hide()
tooltip.toggle()
tooltip.dispose()
tooltip.enable()
tooltip.disable()

// Update tooltip text dynamically
document.getElementById('myBtn').setAttribute('data-bs-title', 'Copied!')
tooltip.update()

Collapse API

const collapseEl = document.getElementById('myCollapse')
const collapse = new bootstrap.Collapse(collapseEl, {
  toggle: false   // don't toggle on instantiation
})

collapse.show()
collapse.hide()
collapse.toggle()

// Events
collapseEl.addEventListener('show.bs.collapse', () => console.log('Expanding'))
collapseEl.addEventListener('shown.bs.collapse', () => console.log('Expanded'))
collapseEl.addEventListener('hide.bs.collapse', () => console.log('Collapsing'))
collapseEl.addEventListener('hidden.bs.collapse', () => console.log('Collapsed'))

Off-Canvas API

const offcanvasEl = document.getElementById('mySidebar')
const offcanvas = new bootstrap.Offcanvas(offcanvasEl, {
  backdrop: true,
  keyboard: true,
  scroll: false
})

offcanvas.show()
offcanvas.hide()
offcanvas.toggle()

Tab API

const tabEl = document.querySelector('#myTab [data-bs-toggle="tab"]')
const tab = new bootstrap.Tab(tabEl)
tab.show()

// Activate a tab by target
const targetTab = document.querySelector('[data-bs-target="#settings"]')
bootstrap.Tab.getOrCreateInstance(targetTab).show()

// Event
document.getElementById('myTab').addEventListener('shown.bs.tab', function(e) {
  console.log('Active tab:', e.target.dataset.bsTarget)
  console.log('Previous tab:', e.relatedTarget.dataset.bsTarget)
})

Popover API

Like tooltips, popovers need JavaScript initialisation:

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

// With options
const popover = new bootstrap.Popover(document.getElementById('myBtn'), {
  title: 'Angular Templates',
  content: 'Get 30% off with code FIRST30 at LettStartDesign.com',
  placement: 'bottom',
  trigger: 'click',
  html: true   // allow HTML in content
})

Bootstrap Event Naming Convention

All Bootstrap events follow this pattern: {event}.bs.{component}

PhasePrefix
Before actionshow, hide, slide
After actionshown, hidden, slid

Examples: show.bs.modal, shown.bs.modal, hide.bs.modal, hidden.bs.modal, show.bs.collapse, shown.bs.tab.

The before-action events (show, hide) can be cancelled with e.preventDefault(). The after-action events (shown, hidden) fire once the CSS transition completes.

Frequently Asked Questions

No. Bootstrap 5 dropped jQuery entirely. It uses vanilla JavaScript and Popper.js (included in the bundle). If you're coming from Bootstrap 3 or 4 with jQuery, you can remove it when upgrading to Bootstrap 5.
Data attributes (data-bs-toggle, data-bs-target) initialize components automatically when Bootstrap JS loads — no JavaScript code needed. The JS API gives programmatic control: new bootstrap.Modal(el).show(). Both approaches work and can be mixed.
Use the static getInstance method: const modal = bootstrap.Modal.getInstance(document.getElementById('myModal')). Returns null if no instance exists yet. Use getOrCreateInstance if you want to create one if it doesn't exist.
Import from the ESM build: import { Modal, Tooltip, Dropdown } from 'bootstrap'. Or import specific components: import Modal from 'bootstrap/js/dist/modal'. This works with Vite, Webpack and other bundlers for tree-shaking.

Need a Full Bootstrap 5 Admin Dashboard?

Get a complete Angular 21 + Bootstrap 5 dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off your first purchase.

Related Components