Components

Bootstrap Carousel: Slides, Autoplay and Captions

By the bootstrap.build team · 5 min read · Published

A Bootstrap carousel is a slideshow: images or content panels that slide or fade from one to the next, with arrows, indicator dots, and optional captions. It works from markup and data attributes, with no initialization code. This guide covers the markup, autoplay, controls and indicators, captions, the fade effect, timing, slides of equal height, dark controls, the JavaScript API, accessibility, and performance. Everything is for Bootstrap 5.3.

<div id="hero-carousel" class="carousel slide" data-bs-ride="carousel">
  <div class="carousel-indicators">
    <button type="button" data-bs-target="#hero-carousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
    <button type="button" data-bs-target="#hero-carousel" data-bs-slide-to="1" aria-label="Slide 2"></button>
    <button type="button" data-bs-target="#hero-carousel" data-bs-slide-to="2" aria-label="Slide 3"></button>
  </div>
  <div class="carousel-inner">
    <div class="carousel-item active">
      <img src="slide-1.jpg" class="d-block w-100" alt="Dashboard overview" width="1200" height="600">
    </div>
    <div class="carousel-item">
      <img src="slide-2.jpg" class="d-block w-100" alt="Theme editor" width="1200" height="600" loading="lazy">
    </div>
    <div class="carousel-item">
      <img src="slide-3.jpg" class="d-block w-100" alt="Export options" width="1200" height="600" loading="lazy">
    </div>
  </div>
  <button class="carousel-control-prev" type="button" data-bs-target="#hero-carousel" 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="#hero-carousel" data-bs-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>
</div>
Part Job
.carousel.slide with an id The wrapper; slide adds the sliding animation
.carousel-inner Holds the slides
.carousel-item One slide; exactly one needs active
.carousel-control-prev, -next The arrows, pointing at the carousel’s id
.carousel-indicators The dots; data-bs-slide-to is the slide number from 0

Without the active class on one slide, the carousel shows nothing. d-block w-100 on images removes the gap under inline images and makes them fill the width.

Autoplay

data-bs-ride Behavior
"carousel" Starts sliding as soon as the page loads
"true" Starts sliding after the visitor first uses the arrows or dots
(none) Never slides on its own

The carousel pauses while the pointer is over it (data-bs-pause="hover", the default) and when the browser tab is hidden.

Timing

Each slide shows for 5 seconds by default. Change it for the whole carousel with data-bs-interval="3000" on the wrapper, or per slide:

<div class="carousel-item active" data-bs-interval="8000">...</div>

Set data-bs-wrap="false" to stop at the last slide instead of looping, data-bs-touch="false" to turn off swiping, and data-bs-keyboard="false" to ignore the arrow keys.

Captions

<div class="carousel-item active">
  <img src="slide-1.jpg" class="d-block w-100" alt="Dashboard overview">
  <div class="carousel-caption d-none d-md-block">
    <h5>Everything in one place</h5>
    <p>See your projects, themes, and exports at a glance.</p>
  </div>
</div>

d-none d-md-block hides captions on phones, where they would cover most of the image. On light images, add a dark gradient behind the text so it stays readable:

.carousel-item::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 50%;
  background: linear-gradient(transparent, rgba(0, 0, 0, .6));
}
.carousel-caption { z-index: 1; }

Fade instead of slide

<div id="quotes" class="carousel slide carousel-fade" data-bs-ride="carousel">...</div>

carousel-fade cross-fades between slides. Slides with different background colors fade best when each .carousel-item has a background, for example bg-body.

Slides with the same height

Images of different sizes make the carousel jump in height. Give every image the same height and crop it:

<img src="slide-2.jpg" class="d-block w-100 object-fit-cover" style="height: 420px;" alt="Theme editor">

Or wrap the images in a ratio container, such as <div class="ratio ratio-21x9">. Both techniques are in the Bootstrap images guide.

Dark arrows and dots

The default controls are white. On light slides, make them dark with data-bs-theme="dark" on the carousel:

<div id="logos" class="carousel slide" data-bs-theme="dark">...</div>

The older carousel-dark class still works but is deprecated in Bootstrap 5.3.

A slide can hold anything, not only images: testimonials, cards, or a grid of logos.

<div class="carousel-item active">
  <blockquote class="text-center px-5 py-4 mb-0">
    <p class="fs-4">"We shipped our redesign in a week."</p>
    <footer class="text-body-secondary">Ana Lopez, product lead</footer>
  </blockquote>
</div>

Bootstrap shows one slide at a time. To show three cards per slide, put a row of three cards in each .carousel-item. A carousel that moves one card at a time isn’t built in; use a library such as Swiper or Splide for that.

const carousel = bootstrap.Carousel.getOrCreateInstance('#hero-carousel')
carousel.next()
carousel.prev()
carousel.to(2)      // go to the third slide
carousel.pause()
carousel.cycle()    // start autoplay

The slide.bs.carousel event fires when a slide starts to change, and slid.bs.carousel when it has finished. Both carry event.from and event.to, the slide numbers:

document.querySelector('#hero-carousel').addEventListener('slid.bs.carousel', event => {
  console.log(`Now showing slide ${event.to + 1}`)
})

Accessibility

  • Give autoplaying carousels a pause button. Content that moves on its own for more than 5 seconds needs a way to stop it. Call carousel.pause() from a visible button.
  • Write real alt text for each slide image, and keep important information in the page text too, since many visitors never see slides after the first.
  • Keep the visually hidden “Previous” and “Next” labels on the arrows, and the aria-label on each indicator.
  • Reduced motion: when a visitor turns on “reduce motion”, Bootstrap removes the sliding transition.

Performance

  • Don’t lazy-load the first slide’s image: it is often the largest image on the page, and lazy-loading it slows the page’s main content. Do add loading="lazy" to the other slides.
  • Set width and height on every image so the page doesn’t shift while they load.
  • Compress slides and serve them in WebP or AVIF, with srcset for phones.

FAQ

Wrap .carousel-item slides in a .carousel-inner, inside a <div class="carousel slide" id="...">. Mark one slide active, and add .carousel-control-prev and .carousel-control-next buttons with data-bs-target pointing to the carousel’s id. Bootstrap’s JavaScript runs it with no extra code.

Add data-bs-ride="carousel" to the carousel to start it when the page loads. Use data-bs-ride="true" to start it only after the visitor first clicks an arrow or dot.

Set data-bs-interval in milliseconds on the carousel, or on a single .carousel-item to change only that slide. The default is 5000, meaning 5 seconds per slide.

Give each image the same fixed height and the object-fit-cover class, for example class="d-block w-100 object-fit-cover" style="height: 420px". The images are cropped to the same size without stretching.

Remove the data-bs-ride attribute; the arrows and dots still work, but the slides only change when clicked. To stop it from JavaScript, call bootstrap.Carousel.getOrCreateInstance(element).pause().

Try it in the builder

Make these changes visually with a live preview, then export clean Bootstrap Sass or CSS.

Open the Builder