Carousel

Bootstrap · Lesson 21 of 28 6 min read Updated

A carousel is a slideshow: one slide is visible at a time, and the others slide in when you click an arrow or after a few seconds. You see them on shop home pages for offers, on college sites for campus photos, and in apps for onboarding screens. Bootstrap's carousel plugin handles the sliding, the controls, touch swiping and keyboard arrows.

In this lesson you will build a carousel with controls, indicators and captions, adjust its timing and effects, control it from JavaScript, and learn when a carousel is the wrong choice.

ElementPurpose
carousel slide with an idThe outer box. slide adds the sliding animation
carousel-innerHolds the slides and hides the ones not shown
carousel-itemOne slide. Exactly one must also have active
carousel-control-prev / -nextPrevious and next buttons
carousel-indicatorsSmall buttons, one per slide
carousel-captionText placed over a slide
HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Carousel</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-3" style="max-width: 720px">
    <div id="campusCarousel" class="carousel slide">
      <div class="carousel-indicators">
        <button type="button" data-bs-target="#campusCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1: Main building"></button>
        <button type="button" data-bs-target="#campusCarousel" data-bs-slide-to="1" aria-label="Slide 2: Library"></button>
        <button type="button" data-bs-target="#campusCarousel" data-bs-slide-to="2" aria-label="Slide 3: Sports ground"></button>
      </div>
      <div class="carousel-inner rounded">
        <div class="carousel-item active">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Main building of the Pune campus at sunrise" width="800" height="500">
          <div class="carousel-caption d-none d-md-block">
            <h2 class="h5">Main building</h2>
            <p>Classrooms, labs and the admission office.</p>
          </div>
        </div>
        <div class="carousel-item">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Library reading hall" width="800" height="500">
          <div class="carousel-caption d-none d-md-block">
            <h2 class="h5">Library</h2>
            <p>Open 8 am to 10 pm, with 40,000 books.</p>
          </div>
        </div>
        <div class="carousel-item">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Cricket and football ground" width="800" height="500">
          <div class="carousel-caption d-none d-md-block">
            <h2 class="h5">Sports ground</h2>
            <p>Cricket nets and a full-size football field.</p>
          </div>
        </div>
      </div>
      <button class="carousel-control-prev" type="button" data-bs-target="#campusCarousel" 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="#campusCarousel" data-bs-slide="next">
        <span class="carousel-control-next-icon" aria-hidden="true"></span>
        <span class="visually-hidden">Next</span>
      </button>
    </div>
    <p class="mt-2 small text-body-secondary" id="slideInfo">Slide 1 of 3</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    document.getElementById("campusCarousel").addEventListener("slid.bs.carousel", (event) => {
      document.getElementById("slideInfo").textContent = `Slide ${event.to + 1} of 3`;
    });
  </script>
</body>
</html>

How the parts connect:

  • Every control and indicator has data-bs-target="#campusCarousel", the carousel's id. data-bs-slide="prev" or "next" moves one step; data-bs-slide-to="1" jumps to a slide by index (counting from 0).
  • The arrow icons are images, so they have aria-hidden="true", and a visually-hidden span gives each button a name. The indicators have aria-labels for the same reason.
  • d-block w-100 on the images removes the gap below inline images and makes every slide fill the same width. Use images with the same shape, or the carousel's height jumps between slides.
  • carousel-caption d-none d-md-block hides captions on phones, where they would cover most of the picture.
  • The slid.bs.carousel event fires after each slide change. Its event.to and event.from are slide indexes, and event.direction is "left" or "right".

This carousel does not move by itself. That is deliberate, as the next section explains.

Autoplay and timing

To start sliding automatically when the page loads, add data-bs-ride="carousel". The options, written as data-bs-* attributes on the carousel or passed in JavaScript:

OptionDefaultMeaning
ridefalse"carousel": autoplay on load. true: autoplay after the user first uses a control
interval5000Milliseconds between slides; also settable per slide with data-bs-interval on a carousel-item
pause"hover"Pauses while the mouse is over it; false never pauses
wraptrueLoops from the last slide back to the first
keyboardtrueLeft and right arrow keys move the slides
touchtrueSwiping works on touch screens
HTML
<div id="offers" class="carousel slide carousel-fade" data-bs-ride="carousel" data-bs-interval="4000">
  <div class="carousel-inner">
    <div class="carousel-item active" data-bs-interval="8000">...first offer stays 8 seconds...</div>
    <div class="carousel-item">...</div>
  </div>
</div>

carousel-fade cross-fades the slides instead of sliding them.

The JavaScript API

JavaScript
const el = document.getElementById("campusCarousel");
const carousel = bootstrap.Carousel.getOrCreateInstance(el, { interval: 3000 });

carousel.next();      // next slide
carousel.prev();      // previous slide
carousel.to(2);       // jump to the third slide
carousel.cycle();     // start autoplay
carousel.pause();     // stop autoplay

Here is a carousel with a working pause/play button, which is how an autoplaying carousel should behave:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Carousel with pause</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-3" style="max-width: 560px">
    <div id="quotes" class="carousel slide border rounded p-4 text-center" data-bs-ride="carousel" data-bs-interval="3000">
      <div class="carousel-inner">
        <div class="carousel-item active"><p class="lead mb-0">"Best samosas in Lucknow." Rohan</p></div>
        <div class="carousel-item"><p class="lead mb-0">"Delivery was hot and on time." Ananya</p></div>
        <div class="carousel-item"><p class="lead mb-0">"The kulfi is worth the trip." Kabir</p></div>
      </div>
    </div>
    <div class="d-flex gap-2 justify-content-center mt-2">
      <button type="button" class="btn btn-sm btn-outline-secondary" id="prevBtn">Previous</button>
      <button type="button" class="btn btn-sm btn-secondary" id="pauseBtn" aria-pressed="false">Pause</button>
      <button type="button" class="btn btn-sm btn-outline-secondary" id="nextBtn">Next</button>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const quotes = bootstrap.Carousel.getOrCreateInstance(document.getElementById("quotes"));
    const pauseBtn = document.getElementById("pauseBtn");
    let paused = false;

    pauseBtn.addEventListener("click", () => {
      paused = !paused;
      if (paused) {
        quotes.pause();
      } else {
        quotes.cycle();
      }
      pauseBtn.textContent = paused ? "Play" : "Pause";
      pauseBtn.setAttribute("aria-pressed", String(paused));
    });
    document.getElementById("prevBtn").addEventListener("click", () => quotes.prev());
    document.getElementById("nextBtn").addEventListener("click", () => quotes.next());
  </script>
</body>
</html>

This testimonial carousel uses text slides, which shows that a slide can contain any HTML, not only images. When a carousel has light slides, add data-bs-theme="dark" to it: the arrows, indicators and captions turn dark so they stay visible.

Common mistakes

  • No slide with active. The carousel shows nothing at all.
  • Images of different sizes. The height jumps with every slide. Crop images to the same ratio.
  • Controls with the wrong data-bs-target. The arrows do nothing. It must be the carousel's id.
  • Autoplay with no way to stop it. Provide a pause control, or do not autoplay.

Key points

  • A carousel is carousel slide > carousel-inner > carousel-item (one active), with optional controls, indicators and captions.
  • Controls and indicators point at the carousel with data-bs-target and use data-bs-slide or data-bs-slide-to.
  • data-bs-ride="carousel" autoplays; data-bs-interval, pause, wrap, keyboard and touch tune behaviour.
  • bootstrap.Carousel.getOrCreateInstance(el) provides next(), prev(), to(), cycle() and pause(), and slid.bs.carousel reports changes.
  • Autoplaying carousels need a pause control; often a static layout is better.

Exercise

Build a carousel of three festival offers for a sweet shop in Jaipur (Diwali, Holi and Raksha Bandhan), using landscape.svg for each slide with a caption. Use the fade effect, add indicators and previous/next controls, and do not autoplay. Below it, add three small buttons labelled with the festival names that jump straight to the matching slide using the JavaScript API.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Festival offers</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-3" style="max-width: 720px">
    <div id="offers" class="carousel slide carousel-fade">
      <div class="carousel-indicators">
        <button type="button" data-bs-target="#offers" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Diwali offer"></button>
        <button type="button" data-bs-target="#offers" data-bs-slide-to="1" aria-label="Holi offer"></button>
        <button type="button" data-bs-target="#offers" data-bs-slide-to="2" aria-label="Raksha Bandhan offer"></button>
      </div>
      <div class="carousel-inner">
        <div class="carousel-item active">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Diwali sweets box" width="800" height="500">
          <div class="carousel-caption"><h2 class="h5">Diwali: 20% off dry-fruit boxes</h2></div>
        </div>
        <div class="carousel-item">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Holi gujiya platter" width="800" height="500">
          <div class="carousel-caption"><h2 class="h5">Holi: gujiya at ₹480 per kg</h2></div>
        </div>
        <div class="carousel-item">
          <img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="d-block w-100" alt="Rakhi gift hamper" width="800" height="500">
          <div class="carousel-caption"><h2 class="h5">Raksha Bandhan: free rakhi with every hamper</h2></div>
        </div>
      </div>
      <button class="carousel-control-prev" type="button" data-bs-target="#offers" 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="#offers" data-bs-slide="next">
        <span class="carousel-control-next-icon" aria-hidden="true"></span><span class="visually-hidden">Next</span>
      </button>
    </div>
    <div class="d-flex gap-2 mt-2">
      <button type="button" class="btn btn-sm btn-outline-primary" data-go="0">Diwali</button>
      <button type="button" class="btn btn-sm btn-outline-primary" data-go="1">Holi</button>
      <button type="button" class="btn btn-sm btn-outline-primary" data-go="2">Raksha Bandhan</button>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const offers = bootstrap.Carousel.getOrCreateInstance(document.getElementById("offers"));
    document.querySelectorAll("[data-go]").forEach((btn) => {
      btn.addEventListener("click", () => offers.to(Number(btn.dataset.go)));
    });
  </script>
</body>
</html>

There is no data-bs-ride, so the carousel waits for the user. carousel-fade changes the transition, and offers.to() jumps to the slide index stored in each button's data-go attribute.

Quick Quiz

1. What happens if no carousel-item has the active class?

2. Which attribute makes a carousel start sliding on page load?

3. How do you jump to the third slide from JavaScript?

4. Why should an autoplaying carousel have a pause button?

Learn Bootstrap with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call