Components

Carousel

Cycle through slides of content one page at a time.

Import

import { Carousel, CarouselDots, CarouselNavigation } from "@dr-max/ui/carousel"

The entry carries its own precompiled CSS (including the styles of the Button, Icon, Spinner and Text parts it composes) — no extra style imports needed. The track itself is powered by keen-slider.

Usage

Pass the data through the slides array — the default slot is a scoped slot that receives each entry as slide, so the carousel stays agnostic about what a slide looks like. slides-per-page sets how many slides form one page:

<script setup lang="ts">
import { Carousel } from "@dr-max/ui/carousel"

const slides = ["One", "Two", "Three", "Four", "Five", "Six"]
</script>

<template>
  <Carousel :slides="slides" :slides-per-page="3" slide-class="px-1">
    <template #default="{ slide }">
      <div class="flex h-32 items-center justify-center rounded-lg bg-blue-50">
        {{ slide }}
      </div>
    </template>
  </Carousel>
</template>

Controls

The #controls scoped slot receives the paging state and callbacks (currentPage, pageCount, showPage, showPrevPage, showNextPage, …) — wire them to CarouselDots and CarouselNavigation. A separate #pagination slot receives pageIds, currentPage, pageCount and showPage for fully custom pagination:

<script setup lang="ts">
import { Carousel, CarouselDots, CarouselNavigation } from "@dr-max/ui/carousel"

const slides = ["One", "Two", "Three", "Four", "Five", "Six"]
</script>

<template>
  <Carousel :slides="slides" :slides-per-page="3" slide-class="px-1">
    <template #default="{ slide }">
      <div class="flex h-32 items-center justify-center rounded-lg bg-blue-50">
        {{ slide }}
      </div>
    </template>
    <template #controls="{ showPrevPage, showNextPage, showPage, pageCount, currentPage }">
      <div class="relative mt-2 flex min-h-12 items-center">
        <CarouselDots :current-page="currentPage" :page-count="pageCount" @show-page="showPage" />
        <CarouselNavigation class="right-1" @click-prev="showPrevPage" @click-next="showNextPage" />
      </div>
    </template>
  </Carousel>
</template>

Scrollbar

force-scrollbar renders a progress scrollbar below the track — useful when dots would be too many:

<template>
  <Carousel :slides="slides" :slides-per-page="3" force-scrollbar>
    <template #default="{ slide }">…</template>
  </Carousel>
</template>

Autoplay and loop

autoplay-interval advances the carousel automatically (milliseconds, zero disables it — the default), and loop lets the track continue past the last slide:

<template>
  <Carousel :slides="slides" :autoplay-interval="5000" loop>
    <template #default="{ slide }">…</template>
  </Carousel>
</template>

Responsive

breakpoints takes keen-slider responsive options, and per-view-auto sizes slides by their own width instead of slides-per-page:

<template>
  <Carousel
    :slides="slides"
    :slides-per-page="1"
    :breakpoints="{
      '(min-width: 768px)': { slides: { perView: 2 } },
      '(min-width: 1024px)': { slides: { perView: 4 } }
    }"
  >
    <template #default="{ slide }">…</template>
  </Carousel>
</template>

Every slide element carries aria-roledescription — localize it with the slide-role-description prop.

There is no playground for the carousel — it requires slide templates (scoped slots), which the playground cannot express. The demo on the Overview tab and the examples above cover the common setups.
Dr. Max Design System • © 2026