Carousel

An accessible carousel built on native scrolling with Data Slot.

About

The carousel uses the @data-slot/carousel primitive. It supports native scrolling, keyboard navigation, optional pointer dragging, looped navigation, and programmatic navigation through DOM events.

The carousel shows one slide at a time. CarouselItem components must be direct children of CarouselContent. Inactive slides are marked with aria-hidden and inert so their content leaves the accessibility tree and tab order.

Installation

Install the component from the @fulldev registry with the shadcn CLI:

npx shadcn@latest add @fulldev/carousel

Manual install:

npm install @data-slot/carousel

Then copy the component files into src/components/ui/carousel and update the imports to match your project structure.

Usage

---
import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from "@/components/ui/carousel"
---

<Carousel>
  <CarouselContent>
    <CarouselItem>...</CarouselItem>
    <CarouselItem>...</CarouselItem>
    <CarouselItem>...</CarouselItem>
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Composition

Carousel
├── CarouselContent
│   ├── CarouselItem
│   └── CarouselItem
├── CarouselPrevious
└── CarouselNext

Options

Pass Data Slot options directly to Carousel.

PropTypeDefaultDescription
defaultIndexnumber0Initial active slide index.
orientation"horizontal" | "vertical""horizontal"Navigation and scrolling axis.
dragbooleanfalseEnables pointer drag and swipe navigation.
loopbooleanfalseWraps previous, next, keyboard, and event navigation.

Vertical

Give a vertical CarouselContent an explicit height so each item can fill its viewport.

Events

Listen for carousel:change on the root to react to a new active slide.

const carousel = document.querySelector('[data-slot="carousel"]')

carousel?.addEventListener("carousel:change", (event) => {
  const { index } = (event as CustomEvent<{ index: number }>).detail
  console.log(index)
})

Dispatch carousel:set on the root to select an index or move by one slide.

carousel?.dispatchEvent(
  new CustomEvent("carousel:set", { detail: { index: 2 } })
)

carousel?.dispatchEvent(
  new CustomEvent("carousel:set", { detail: { action: "next" } })
)

The root exposes its current index as data-index. Each item exposes data-state="active" or data-state="inactive". Data Slot also keeps the previous and next controls’ disabled and aria-disabled states in sync.

API Reference

See the GitHub source code for component props and the @data-slot/carousel README for controller methods, events, keyboard behavior, and accessibility details.