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}
| Phase | Prefix |
|---|---|
| Before action | show, hide, slide |
| After action | shown, 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
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.