Bootstrap Carousel: Slides, Autoplay and Captions
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.
A working carousel
<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.
Carousel of cards or text
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.
Control a carousel with JavaScript
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
alttext 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-labelon 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
widthandheighton every image so the page doesn’t shift while they load. - Compress slides and serve them in WebP or AVIF, with
srcsetfor phones.
FAQ
How do I make a carousel in Bootstrap 5?
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.
How do I make a Bootstrap carousel autoplay?
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.
How do I change the speed of a Bootstrap carousel?
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.
How do I make all Bootstrap carousel images the same height?
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.
How do I stop a Bootstrap carousel from sliding automatically?
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