Skip to content
Bootstrap Interactive Components — Modals, Carousel & Tooltips Step-by-Step

Bootstrap Interactive Components — Modals, Carousel & Tooltips Step-by-Step

DodaTech Updated Jun 6, 2026 11 min read

Interactive components bring pages to life with dialogs, tooltips, notifications, and slideshows — all driven by Bootstrap’s JavaScript with zero jQuery required.

What You’ll Learn

  • Open and customize modals with sizes, centering, and scrollable content
  • Initialize tooltips and popovers with JavaScript options
  • Show toast notifications programmatically
  • Build offcanvas side panels for navigation or filters
  • Create auto-playing carousels with controls and indicators
  • Use scrollspy to highlight nav links based on scroll position

Why Interactive Components Matter

Users expect rich interactions — clicking a button should show feedback, hovering should reveal hints, and long pages should highlight the current section. Building these from scratch requires complex JavaScript.

At DodaTech, Durga Antivirus Pro uses modals for scan result details, Doda Browser uses toasts for download notifications, and DodaZIP uses offcanvas for file properties — all implemented with Bootstrap’s interactive components.

Your Learning Path

    flowchart LR
  A[Components 2: Nav, Navbar] --> B[Components 3: Modals, Carousel, Tooltips]
  B --> C[Bootstrap Forms]
  C --> D[Bootstrap Utilities]
  B:::current

  classDef current fill:#0d6efd,color:#fff,stroke:#0a58ca,stroke-width:2px
  
Prerequisites: You MUST include bootstrap.bundle.min.js (which contains Popper.js). JavaScript basics (variables, functions, event listeners) are required for tooltips/popovers and the JavaScript API. Have the Bootstrap Components Part 2 tutorial ready.

Modals — Dialog Boxes

Modals are overlay dialogs that appear on top of the page with a backdrop. Think of them as a pop-up window that keeps users focused on one task.

<!-- Trigger button: data-bs-toggle="modal" tells Bootstrap to open
     a modal. data-bs-target="#exampleModal" identifies which modal. -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
    Launch Modal
</button>

<!-- The modal itself: placed anywhere in the body (usually near the end).
     modal fade = fade-in animation.
     tabindex="-1" = required for focus management inside the modal. -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-hidden="true">
    <!-- modal-dialog: controls the size and positioning -->
    <div class="modal-dialog">
        <div class="modal-content">
            <!-- modal-header: title area with close button -->
            <div class="modal-header">
                <h5 class="modal-title">Modal Title</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <!-- modal-body: main content area -->
            <div class="modal-body">
                <p>Modal content goes here.</p>
            </div>
            <!-- modal-footer: action buttons at the bottom -->
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary">Save Changes</button>
            </div>
        </div>
    </div>
</div>

Modal Sizes and Variants

<!-- Sizes: add size class to modal-dialog -->
<div class="modal-dialog modal-sm">       <!-- Small (~300px) -->
<div class="modal-dialog">                <!-- Default (~500px) -->
<div class="modal-dialog modal-lg">       <!-- Large (~800px) -->
<div class="modal-dialog modal-xl">       <!-- Extra large (~1140px) -->

<!-- Centered vertically -->
<div class="modal-dialog modal-dialog-centered">

<!-- Scrollable body (when content is very long) -->
<div class="modal-dialog modal-dialog-scrollable">

<!-- Fullscreen (covers the entire viewport) -->
<div class="modal-dialog modal-fullscreen">

<!-- Static backdrop: clicking outside doesn't close the modal -->
<div class="modal" data-bs-backdrop="static" data-bs-keyboard="false">

JavaScript API for Modals

// Create and control a modal programmatically
var myModal = new bootstrap.Modal(document.getElementById("exampleModal"));
myModal.show();    // Open the modal
myModal.hide();    // Close the modal
myModal.toggle();  // Toggle open/close

// Options
var modal = new bootstrap.Modal("#myModal", {
    backdrop: "static",  // "static" = don't close on backdrop click
    keyboard: false,     // Don't close on Escape key
    focus: true          // Auto-focus modal
});

// Events — useful for analytics or chaining actions
document.getElementById("exampleModal").addEventListener("show.bs.modal", function(e) {
    console.log("Modal about to show");
});
document.getElementById("exampleModal").addEventListener("shown.bs.modal", function(e) {
    console.log("Modal shown — animation complete");
});
document.getElementById("exampleModal").addEventListener("hidden.bs.modal", function(e) {
    console.log("Modal fully hidden — animation complete");
});

Tooltips — Small Hover Hints

Tooltips show a small text label when hovering over an element. Unlike other components, tooltips must be initialized with JavaScript.

<button class="btn btn-secondary" data-bs-toggle="tooltip" title="I'm a tooltip!">
    Hover me
</button>
// Initialize ALL tooltips on the page (call this once)
var tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
var tooltipList = [...tooltipTriggerList].map(function(el) {
    return new bootstrap.Tooltip(el);
});

// With options
var tooltip = new bootstrap.Tooltip(element, {
    placement: "right",        // top, bottom, left, right, auto
    trigger: "hover",          // click, hover, focus, manual
    html: false,               // Allow HTML content in title
    delay: { show: 200, hide: 100 },
    container: "body"
});

// Methods
tooltip.show();
tooltip.hide();
tooltip.toggle();
tooltip.dispose();  // Clean up when removing the element

Why manual initialization? Tooltips need to calculate their position relative to the trigger element. Bootstrap defers this to Popper.js, which requires JavaScript to position them correctly.

Popovers — Larger Hover/Click Information

Popovers are like tooltips but with a title, body, and more content:

<button class="btn btn-lg btn-danger" data-bs-toggle="popover"
    title="Popover Title"
    data-bs-content="And here's some amazing content. It's very engaging.">
    Click to toggle popover
</button>
// Initialize all popovers
var popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
var popoverList = [...popoverTriggerList].map(function(el) {
    return new bootstrap.Popover(el);
});

// With options
var popover = new bootstrap.Popover(element, {
    placement: "auto",
    trigger: "click",
    html: true,
    delay: { show: 0, hide: 0 }
});

Toasts — Notification Messages

Toasts are lightweight, auto-dismissing notifications that appear in a fixed position, like smartphone notifications.

<!-- Toast container: positions all toasts at the bottom-right -->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
    <!-- The toast itself. role="alert" and aria-live="assertive" are
         for accessibility — screen readers announce toast content. -->
    <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"></button>
        </div>
        <div class="toast-body">
            Hello! This is a toast notification.
        </div>
    </div>
</div>

<button class="btn btn-primary" onclick="showToast()">Show Toast</button>

<script>
    function showToast() {
        // Create a new Toast instance with options
        var toast = new bootstrap.Toast(document.getElementById("liveToast"), {
            animation: true,
            autohide: true,    // Auto-hide after delay
            delay: 5000        // Show for 5 seconds
        });
        toast.show();  // Display the toast
    }
</script>

Offcanvas — Side Panels

Offcanvas creates slide-in panels from any edge — useful for mobile navigation, shopping carts, or filter panels:

<button class="btn btn-primary" data-bs-toggle="offcanvas" data-bs-target="#offcanvasExample">
    Open Offcanvas
</button>

<!-- offcanvas-start = slides from left (default).
     tabindex="-1" = required for focus management. -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasExample">
    <div class="offcanvas-header">
        <h5 class="offcanvas-title">Offcanvas</h5>
        <button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
    </div>
    <div class="offcanvas-body">
        <p>Side panel content here.</p>
    </div>
</div>

<!-- Placements -->
<div class="offcanvas offcanvas-start">   <!-- Left (default) -->
<div class="offcanvas offcanvas-end">     <!-- Right -->
<div class="offcanvas offcanvas-top">     <!-- Top -->
<div class="offcanvas offcanvas-bottom">  <!-- Bottom -->

<!-- With backdrop (darkens page behind the panel) -->
<div class="offcanvas offcanvas-start" data-bs-backdrop="true">

Carousel — Image/Content Slideshows

Carousels cycle through elements (usually images) with previous/next controls:

<div id="carouselExample" class="carousel slide" data-bs-ride="carousel">
    <!-- Indicators: the dots at the bottom. Each button targets a slide
         via data-bs-slide-to (0-indexed). -->
    <div class="carousel-indicators">
        <button type="button" data-bs-target="#carouselExample" data-bs-slide-to="0" class="active"></button>
        <button type="button" data-bs-target="#carouselExample" data-bs-slide-to="1"></button>
        <button type="button" data-bs-target="#carouselExample" data-bs-slide-to="2"></button>
    </div>

    <!-- carousel-inner wraps all slides. Only one slide has .active. -->
    <div class="carousel-inner">
        <div class="carousel-item active">
            <!-- d-block w-100 ensures the image fills the carousel width -->
            <img src="https://picsum.photos/seed/slide1/800/400" class="d-block w-100" alt="Slide 1">
        </div>
        <div class="carousel-item">
            <img src="https://picsum.photos/seed/slide2/800/400" class="d-block w-100" alt="Slide 2">
        </div>
        <div class="carousel-item">
            <img src="https://picsum.photos/seed/slide3/800/400" class="d-block w-100" alt="Slide 3">
        </div>
    </div>

    <!-- Previous/Next controls -->
    <button class="carousel-control-prev" type="button" data-bs-target="#carouselExample" data-bs-slide="prev">
        <span class="carousel-control-prev-icon"></span>
    </button>
    <button class="carousel-control-next" type="button" data-bs-target="#carouselExample" data-bs-slide="next">
        <span class="carousel-control-next-icon"></span>
    </button>
</div>

Carousel Options

<!-- Data attributes control behavior -->
<div class="carousel slide" data-bs-ride="carousel">        <!-- Auto-play -->
<div class="carousel slide" data-bs-ride="false">           <!-- Manual only -->
<div class="carousel slide" data-bs-interval="2000">        <!-- 2 seconds per slide -->
<div class="carousel slide" data-bs-pause="hover">          <!-- Pause on mouse hover -->
<div class="carousel slide" data-bs-wrap="false">           <!-- No wrap (stops at end) -->
// JavaScript API
var carousel = new bootstrap.Carousel(document.getElementById("carouselExample"), {
    interval: 3000,
    wrap: true,
    pause: "hover"
});
carousel.cycle();     // Start auto-playing
carousel.pause();     // Stop auto-playing
carousel.prev();      // Go to previous slide
carousel.next();      // Go to next slide
carousel.to(2);       // Go to slide index 2

Scrollspy — Auto-Highlighting Navigation

Scrollspy watches scroll position and automatically highlights the nav link for the current section:

<!-- The nav being tracked: links must match section IDs -->
<nav id="navbar-scrollspy" class="navbar navbar-light bg-light px-3 sticky-top">
    <ul class="nav nav-pills">
        <li class="nav-item"><a class="nav-link" href="#section1">Section 1</a></li>
        <li class="nav-item"><a class="nav-link" href="#section2">Section 2</a></li>
        <li class="nav-item"><a class="nav-link" href="#section3">Section 3</a></li>
    </ul>
</nav>

<!-- The scrollable container: data-bs-spy enables spying,
     data-bs-target links it to the nav, data-bs-offset adjusts trigger point -->
<div data-bs-spy="scroll" data-bs-target="#navbar-scrollspy" data-bs-offset="50"
     tabindex="0" style="height: 300px; overflow-y: scroll;">
    <h4 id="section1">Section 1</h4>
    <p>Long content here...</p>
    <h4 id="section2">Section 2</h4>
    <p>More content...</p>
    <h4 id="section3">Section 3</h4>
    <p>Even more content...</p>
</div>

Common Mistakes

1. Forgetting to initialize tooltips and popovers

Unlike modals and dropdowns, tooltips and popovers must be initialized via JavaScript. Using data-bs-toggle alone doesn’t work.

2. Not including Popper.js for tooltips/popovers

Tooltips and popovers require Popper.js for positioning. Use bootstrap.bundle.min.js which includes Popper built-in.

3. Missing tabindex="-1" on modals

Without tabindex="-1", focus management in modals doesn’t work. Users can tab behind the modal, breaking accessibility.

4. Putting forms or heavy content inside carousel slides

Carousels are for visual showcases, not interactive content. Use modals or offcanvas for forms.

5. Forgetting the toast positioning container

Toasts need a .toast-container wrapper with positioning. Without it, toasts appear at unexpected (often invisible) positions.

Practice Questions

  1. Why don’t tooltips work with just HTML attributes like modals do? Answer: Tooltips need Popper.js to calculate their position relative to the trigger element. Modals are simpler overlays that don’t need position calculation.

  2. What’s the difference between data-bs-backdrop="static" and the default backdrop? Answer: The default backdrop closes the modal when clicked outside. static keeps the modal open even when clicking the backdrop.

  3. How do you make a carousel stop auto-playing? Answer: Use data-bs-ride="false" or omit data-bs-ride entirely. You can also use the JavaScript API: carousel.pause().

  4. What event fires when a modal has finished its close animation? Answer: hidden.bs.modal. The hide.bs.modal event fires immediately, but hidden.bs.modal waits until the animation completes.

  5. How do you clean up a tooltip when removing its trigger element? Answer: Call .dispose() on the tooltip instance: tooltip.dispose(). This removes event listeners and prevents memory leaks.

Challenge: Build a product showcase page with a hero carousel, a “Buy Now” button that opens a modal with a quantity selector, tooltips on feature items, and a toast notification when the order is confirmed.

FAQ

Why don’t my tooltips work with just HTML attributes?
Tooltips require JavaScript initialization. Use new bootstrap.Tooltip(element) for each tooltip, or query all elements with data-bs-toggle="tooltip".
What is the difference between tooltip and popover?
Tooltips show a small text label on hover. Popovers show a title + body with more content. Both need Popper.js and manual initialization.
How do I prevent a modal from closing when clicking outside?
Add data-bs-backdrop="static" and data-bs-keyboard="false" to the modal, or use JavaScript: { backdrop: "static", keyboard: false }.
Can I auto-play a carousel?
Yes, add data-bs-ride="carousel" to the carousel element. The slides will auto-advance every 5 seconds (default interval).
What is offcanvas used for?
Offcanvas creates slide-in panels for navigation menus, filters, shopping carts, or any secondary content that doesn’t need to be always visible.
How do I destroy a Bootstrap component?
Call .dispose() on the instance: modal.dispose(), tooltip.dispose(), etc.

Try It Yourself

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Product Showcase</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
        <div class="carousel-indicators">
            <button data-bs-target="#heroCarousel" data-bs-slide-to="0" class="active"></button>
            <button data-bs-target="#heroCarousel" data-bs-slide-to="1"></button>
            <button data-bs-target="#heroCarousel" data-bs-slide-to="2"></button>
        </div>
        <div class="carousel-inner">
            <div class="carousel-item active">
                <img src="https://picsum.photos/seed/hero1/1200/400" class="d-block w-100" alt="Slide 1">
                <div class="carousel-caption d-none d-md-block">
                    <h5>New Collection</h5>
                    <p>Discover our latest products</p>
                </div>
            </div>
            <div class="carousel-item">
                <img src="https://picsum.photos/seed/hero2/1200/400" class="d-block w-100" alt="Slide 2">
            </div>
            <div class="carousel-item">
                <img src="https://picsum.photos/seed/hero3/1200/400" class="d-block w-100" alt="Slide 3">
            </div>
        </div>
        <button class="carousel-control-prev" data-bs-target="#heroCarousel" data-bs-slide="prev">
            <span class="carousel-control-prev-icon"></span>
        </button>
        <button class="carousel-control-next" data-bs-target="#heroCarousel" data-bs-slide="next">
            <span class="carousel-control-next-icon"></span>
        </button>
    </div>

    <div class="container my-5">
        <div class="row">
            <div class="col-md-6">
                <img src="https://picsum.photos/seed/product/600/600" class="img-fluid rounded" alt="Product">
            </div>
            <div class="col-md-6">
                <h1>Premium Wireless Headphones</h1>
                <p class="lead">Experience crystal-clear audio with our premium headphones.</p>
                <h2 class="text-primary">$199.99</h2>
                <button class="btn btn-primary btn-lg me-2" data-bs-toggle="modal" data-bs-target="#orderModal">Buy Now</button>
                <button class="btn btn-outline-secondary btn-lg" data-bs-toggle="tooltip" title="Add to wishlist">♥ Wishlist</button>
                <hr>
                <h5>Features</h5>
                <ul>
                    <li>Active Noise Cancellation</li>
                    <li>30-hour battery life</li>
                    <li data-bs-toggle="tooltip" title="Hi-Res Audio Certified">Hi-Res Audio support</li>
                </ul>
                <button class="btn btn-sm btn-info" data-bs-toggle="popover"
                    title="Shipping Info"
                    data-bs-content="Free shipping on orders over $50. Delivery: 3-5 business days.">
                    Shipping Info
                </button>
            </div>
        </div>
    </div>

    <div class="modal fade" id="orderModal" tabindex="-1">
        <div class="modal-dialog modal-dialog-centered">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Place Order</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <p>Premium Wireless Headphones — <strong>$199.99</strong></p>
                    <div class="mb-3">
                        <label class="form-label">Quantity</label>
                        <input type="number" class="form-control" value="1" min="1">
                    </div>
                </div>
                <div class="modal-footer">
                    <button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button class="btn btn-primary">Confirm Order</button>
                </div>
            </div>
        </div>
    </div>

    <div class="toast-container position-fixed bottom-0 end-0 p-3">
        <div id="successToast" class="toast" role="alert">
            <div class="toast-header bg-success text-white">
                <strong class="me-auto">Success</strong>
                <button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
            </div>
            <div class="toast-body">Item added to cart!</div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        var tooltipList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
        [...tooltipList].map(function(el) { return new bootstrap.Tooltip(el); });

        var popoverList = document.querySelectorAll('[data-bs-toggle="popover"]');
        [...popoverList].map(function(el) { return new bootstrap.Popover(el); });

        document.querySelector("#orderModal .btn-primary").addEventListener("click", function() {
            var toast = new bootstrap.Toast(document.getElementById("successToast"));
            toast.show();
            var modal = bootstrap.Modal.getInstance(document.getElementById("orderModal"));
            modal.hide();
        });
    </script>
</body>
</html>

This sandbox demonstrates: carousel, modal, tooltips, popovers, toasts, and JavaScript component initialization.

What’s Next

TopicDescription
Bootstrap FormsInputs, validation, and form layout
Bootstrap UtilitiesFlexbox, spacing, display helpers
Bootstrap AdvancedDark mode, Sass, JavaScript API
JavaScript EventsUnderstanding event-driven programming

What’s Next

Congratulations on completing this Bootstrap Components 3 tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro