Skip to content

Documentation

On this page

Carousel

A sliding gallery with Bootstrap’s carousel classes. The browser owns snapping, swipe, and (where supported) previous/next and indicators — no Carousel plugin.

Sizes are minified and gzip. A plugin figure includes the Bootstrap runtime that plugin imports. Shipping every plugin is bootstrap.bundle.min.js at 23.3 KB gzip, not the sum of these rows.
What runs it JavaScript
Bootstrap 5.3 Carousel plugin (slide, swipe, indicators, keyboard) 6.7 KB gzip
This repo CSS scroll snap. Optional pe-carousel when ::scroll-button is missing. 0 KB. Optional behavior is 1.4 KB gzip.

When that API is missing, this page also loads invoker-commands.js (0.8 KB gzip). See Polyfills.

Markup this component accepts. The full inventory is /components.json.
Optional <pe-carousel> · src/behaviors/carousel.js . Prev, next, and pip controls where ::scroll-button is missing. Inert where it is not.
Avoid
  • No autoplay, no .carousel-fade crossfade, and no wrap from the last slide to the first.
  • commandfor is the .carousel-inner id with no #.

How it works

  • .carousel-inner is a horizontal scroller with scroll-snap-type: x mandatory. That replaces Bootstrap’s transform + JavaScript.
  • Every .carousel-item is in the row (flex: 0 0 100%). .active is not required for the first slide to show.
  • Do not add HTML .carousel-control-prev / .carousel-control-next or .carousel-indicators. Those need script. The browser generates equivalent controls from CSS.
  • Put data-label on each item so ::scroll-marker has an accessible name (content: attr(data-label)).
  • Without ::scroll-button, swipe, drag the scrollbar, or focus the scroller and use the keyboard. HTML buttons, pips, and mouse panning are the fallback, not this markup.
  • scroll-behavior: smooth follows prefers-reduced-motion.

Slides

Swipe or use the generated previous/next buttons. Indicator pips jump to a slide. All three slides stay in the DOM — none are display: none.

HTML
<div class="carousel slide">
  <div class="carousel-inner">
    <div class="carousel-item" data-label="Slide 1">
    <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Basic first slide</div>
  </div>
  <div class="carousel-item" data-label="Slide 2">
    <div class="d-flex w-100 align-items-center justify-content-center bg-dark text-white" style="min-height: 16rem">Basic second slide</div>
  </div>
  <div class="carousel-item" data-label="Slide 3">
    <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Basic third slide</div>
  </div>
  </div>
</div>

Captions

Add .carousel-caption inside any .carousel-item. Hide them on small viewports with .d-none .d-md-block as in Bootstrap’s docs.

HTML
<div class="carousel slide">
  <div class="carousel-inner">
    <div class="carousel-item" data-label="First slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">First slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>First slide label</h5>
        <p>Some representative placeholder content for the first slide.</p>
      </div>
    </div>
    <div class="carousel-item" data-label="Second slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-dark text-white" style="min-height: 16rem">Second slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>Second slide label</h5>
        <p>Some representative placeholder content for the second slide.</p>
      </div>
    </div>
    <div class="carousel-item" data-label="Third slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Third slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>Third slide label</h5>
        <p>Some representative placeholder content for the third slide.</p>
      </div>
    </div>
  </div>
</div>

Dark controls

Set data-bs-theme="dark" on the carousel (or a parent) so captions and control icons invert. .carousel-dark is deprecated in Bootstrap 5.3.

HTML
<div class="carousel slide" data-bs-theme="dark">
  <div class="carousel-inner">
    <div class="carousel-item" data-label="First slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-body-secondary" style="min-height: 16rem">First slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>First slide label</h5>
        <p>Some representative placeholder content for the first slide.</p>
      </div>
    </div>
    <div class="carousel-item" data-label="Second slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Second slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>Second slide label</h5>
        <p>Some representative placeholder content for the second slide.</p>
      </div>
    </div>
    <div class="carousel-item" data-label="Third slide">
      <div class="d-flex w-100 align-items-center justify-content-center bg-dark text-white" style="min-height: 16rem">Third slide</div>
      <div class="carousel-caption d-none d-md-block">
        <h5>Third slide label</h5>
        <p>Some representative placeholder content for the third slide.</p>
      </div>
    </div>
  </div>
</div>

Accessibility

Put data-label on each .carousel-item so ::scroll-marker has an accessible name. Native scroll buttons use content: "" / "Previous" and "Next".

On <pe-carousel>, the current pip is aria-current="true". Previous and next disable at the ends. scroll-behavior: smooth follows prefers-reduced-motion.

Browser support

Three columns, not one baseline. The same row is on the overview.
No script Polyfill No fallback
scroll-snap-type is widely available. Swipe works. None ::scroll-button and ::scroll-marker: Chrome 135+ only. Optional pe-carousel covers buttons and pips elsewhere. No autoplay, fade, or wrap.

Browsers without scroll buttons

<pe-carousel> is the carousel, with class="carousel". Load src/behaviors/carousel.js only on pages that use it. Where ::scroll-button exists, the element does nothing: the generated buttons and pips stay, and the HTML controls are hidden. Where it does not, the two buttons scroll one .carousel-item via command="--prev" and command="--next". commandfor is the .carousel-inner id, with no hash. A mouse drag pans that scroller; touch scrolling stays native. Links inside a slide still click. Indicator buttons use command="--slide" in the same order as the slides. The current one is aria-current="true".

Fallback controls

In Chrome the HTML buttons and pips are hidden and the generated controls remain. In Firefox and Safari the buttons move one slide, the pips jump to a slide, and dragging with the mouse pans.

HTML
<script type="module" src="./node_modules/bootstrap-html/src/behaviors/carousel.js"></script>

<pe-carousel class="carousel slide">
  <button type="button" class="carousel-control-prev" command="--prev" commandfor="fallback-carousel">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Previous</span>
  </button>
  <div class="carousel-inner" id="fallback-carousel">
    <div class="carousel-item" data-label="Slide 1">
    <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Fallback first slide</div>
  </div>
  <div class="carousel-item" data-label="Slide 2">
    <div class="d-flex w-100 align-items-center justify-content-center bg-dark text-white" style="min-height: 16rem">Fallback second slide</div>
  </div>
  <div class="carousel-item" data-label="Slide 3">
    <div class="d-flex w-100 align-items-center justify-content-center bg-secondary text-white" style="min-height: 16rem">Fallback third slide</div>
  </div>
  </div>
  <button type="button" class="carousel-control-next" command="--next" commandfor="fallback-carousel">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>
  <div class="carousel-indicators">
    <button type="button" command="--slide" commandfor="fallback-carousel" aria-label="Slide 1"></button>
    <button type="button" command="--slide" commandfor="fallback-carousel" aria-label="Slide 2"></button>
    <button type="button" command="--slide" commandfor="fallback-carousel" aria-label="Slide 3"></button>
  </div>
</pe-carousel>