---
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
---
<Carousel drag class="w-[calc(100%-2rem)] max-w-xl">
<CarouselContent>
<CarouselItem>
<div
class="bg-muted flex aspect-video items-center justify-center rounded-md"
>
<span class="text-3xl font-semibold">1</span>
</div>
</CarouselItem>
<CarouselItem>
<div
class="bg-muted flex aspect-video items-center justify-center rounded-md"
>
<span class="text-3xl font-semibold">2</span>
</div>
</CarouselItem>
<CarouselItem>
<div
class="bg-muted flex aspect-video items-center justify-center rounded-md"
>
<span class="text-3xl font-semibold">3</span>
</div>
</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>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.
| Prop | Type | Default | Description |
|---|---|---|---|
defaultIndex | number | 0 | Initial active slide index. |
orientation | "horizontal" | "vertical" | "horizontal" | Navigation and scrolling axis. |
drag | boolean | false | Enables pointer drag and swipe navigation. |
loop | boolean | false | Wraps previous, next, keyboard, and event navigation. |
---
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
---
<Carousel defaultIndex={1} drag loop class="w-[calc(100%-2rem)] max-w-xl">
<CarouselContent>
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>First slide</CarouselItem
>
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>Second slide</CarouselItem
>
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>Third slide</CarouselItem
>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Vertical
Give a vertical CarouselContent an explicit height so each item can fill its
viewport.
---
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
---
<Carousel orientation="vertical" drag class="my-8 w-full max-w-xl">
<CarouselContent class="h-64">
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>First slide</CarouselItem
>
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>Second slide</CarouselItem
>
<CarouselItem
class="bg-muted flex min-h-40 items-center justify-center rounded-md"
>Third slide</CarouselItem
>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>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.